From 2244b1161e03fe5e60eeb1a2d2f18b99d31c1070 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 8 Apr 2023 15:35:49 +0100 Subject: [PATCH] api docs (#2240) * api docs * more docstrings * logs * docs shakeup * fix notes, added intro to all apis * Remove defaults to * add note to events * note * use fira code --- .../python/material/_base/class.html | 15 +- .../material/_base/docstring/admonition.html | 5 + .../python/material/_base/function.html | 5 +- .../python/material/_base/signature.html | 4 +- docs/api/button.md | 3 - docs/api/checkbox.md | 1 - docs/api/data_table.md | 1 - docs/api/directory_tree.md | 1 - docs/api/footer.md | 1 - docs/api/header.md | 1 - docs/api/input.md | 1 - docs/api/label.md | 1 - docs/api/list_item.md | 1 - docs/api/list_view.md | 1 - docs/api/loading_indicator.md | 1 - docs/api/logger.md | 1 + docs/api/markdown.md | 1 - docs/api/markdown_viewer.md | 1 - docs/api/message_pump.md | 4 - docs/api/option_list.md | 3 - docs/api/placeholder.md | 2 - docs/api/radiobutton.md | 1 - docs/api/radioset.md | 1 - docs/api/static.md | 1 - docs/api/switch.md | 1 - docs/api/tabbed_content.md | 2 - docs/api/tabs.md | 2 - docs/api/text_log.md | 1 - docs/api/toggle_button.md | 1 - docs/api/tree.md | 4 - docs/api/welcome.md | 1 - docs/api/work.md | 1 + docs/blog/posts/release0-11-0.md | 2 +- docs/stylesheets/custom.css | 7 +- docs/widgets/_template.md | 9 +- docs/widgets/button.md | 9 +- docs/widgets/checkbox.md | 10 +- docs/widgets/content_switcher.md | 11 +- docs/widgets/data_table.md | 27 ++- docs/widgets/directory_tree.md | 12 +- docs/widgets/footer.md | 8 +- docs/widgets/header.md | 7 +- docs/widgets/input.md | 12 +- docs/widgets/label.md | 7 +- docs/widgets/list_item.md | 11 +- docs/widgets/list_view.md | 12 +- docs/widgets/loading_indicator.md | 6 +- docs/widgets/markdown.md | 18 +- docs/widgets/markdown_viewer.md | 10 +- docs/widgets/option_list.md | 13 +- docs/widgets/placeholder.md | 13 +- docs/widgets/radiobutton.md | 10 +- docs/widgets/radioset.md | 11 +- docs/widgets/static.md | 9 +- docs/widgets/switch.md | 9 +- docs/widgets/tabbed_content.md | 22 +- docs/widgets/tabs.md | 19 +- docs/widgets/text_log.md | 7 +- docs/widgets/tree.md | 15 +- mkdocs-nav.yml | 27 +-- src/textual/__init__.py | 1 - src/textual/_animator.py | 28 +-- src/textual/_cache.py | 4 +- src/textual/_work_decorator.py | 9 +- src/textual/_worker_manager.py | 8 + src/textual/app.py | 101 +++++---- src/textual/await_remove.py | 7 +- src/textual/binding.py | 19 +- src/textual/color.py | 114 +++++------ src/textual/containers.py | 6 + src/textual/coordinate.py | 5 + src/textual/css/_error_tools.py | 2 +- src/textual/css/_help_renderables.py | 2 +- src/textual/css/_styles_builder.py | 2 +- src/textual/css/query.py | 30 ++- src/textual/css/styles.py | 14 +- src/textual/css/stylesheet.py | 13 +- src/textual/css/tokenizer.py | 2 +- src/textual/demo.py | 4 +- src/textual/design.py | 6 - src/textual/dom.py | 81 ++++++-- src/textual/events.py | 193 ++++++++++++++++-- src/textual/geometry.py | 153 ++++---------- src/textual/logging.py | 10 + src/textual/message.py | 11 +- src/textual/message_pump.py | 27 ++- src/textual/pilot.py | 6 + src/textual/reactive.py | 28 ++- src/textual/renderables/underline_bar.py | 2 +- src/textual/screen.py | 6 + src/textual/scroll_view.py | 5 + src/textual/strip.py | 12 +- src/textual/timer.py | 10 +- src/textual/walk.py | 15 +- src/textual/widget.py | 37 +++- src/textual/widgets/_markdown.py | 6 +- src/textual/widgets/_placeholder.py | 2 +- src/textual/widgets/_static.py | 13 +- src/textual/widgets/_switch.py | 4 +- src/textual/widgets/_toggle_button.py | 2 +- src/textual/widgets/_tree.py | 4 +- src/textual/worker.py | 5 + tests/test_binding.py | 24 +-- 103 files changed, 879 insertions(+), 557 deletions(-) create mode 100644 docs/_templates/python/material/_base/docstring/admonition.html delete mode 100644 docs/api/button.md delete mode 100644 docs/api/checkbox.md delete mode 100644 docs/api/data_table.md delete mode 100644 docs/api/directory_tree.md delete mode 100644 docs/api/footer.md delete mode 100644 docs/api/header.md delete mode 100644 docs/api/input.md delete mode 100644 docs/api/label.md delete mode 100644 docs/api/list_item.md delete mode 100644 docs/api/list_view.md delete mode 100644 docs/api/loading_indicator.md create mode 100644 docs/api/logger.md delete mode 100644 docs/api/markdown.md delete mode 100644 docs/api/markdown_viewer.md delete mode 100644 docs/api/option_list.md delete mode 100644 docs/api/placeholder.md delete mode 100644 docs/api/radiobutton.md delete mode 100644 docs/api/radioset.md delete mode 100644 docs/api/static.md delete mode 100644 docs/api/switch.md delete mode 100644 docs/api/tabbed_content.md delete mode 100644 docs/api/tabs.md delete mode 100644 docs/api/text_log.md delete mode 100644 docs/api/toggle_button.md delete mode 100644 docs/api/tree.md delete mode 100644 docs/api/welcome.md diff --git a/docs/_templates/python/material/_base/class.html b/docs/_templates/python/material/_base/class.html index 021ff0532..120fe338f 100644 --- a/docs/_templates/python/material/_base/class.html +++ b/docs/_templates/python/material/_base/class.html @@ -13,7 +13,9 @@ {% set show_full_path = config.show_object_full_path %} {% endif %} - {% if not root or config.show_root_heading %} + + + {% if 1 %} {% filter heading(heading_level, role="class", @@ -34,7 +36,7 @@ {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} {% endif %} - {% with labels = class.labels %} + {% with labels = ['class'] %} {% include "labels.html" with context %} {% endwith %} @@ -43,11 +45,10 @@ {% if config.separate_signature and config.merge_init_into_class %} {% if "__init__" in class.members %} {% with function = class.members["__init__"] %} - {% filter highlight(language="python", inline=False) %} -class {% filter format_signature(config.line_length) %} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {% include "signature.html" with context %} - {% endfilter %} + {% filter highlight(language="python", inline=False) -%} + def {% filter format_signature(config.line_length) %} +__init__{% include "signature.html" with context %} + {% endfilter %}: {% endfilter %} {% endwith %} {% endif %} diff --git a/docs/_templates/python/material/_base/docstring/admonition.html b/docs/_templates/python/material/_base/docstring/admonition.html new file mode 100644 index 000000000..2105eab71 --- /dev/null +++ b/docs/_templates/python/material/_base/docstring/admonition.html @@ -0,0 +1,5 @@ +{{ log.debug("Rendering admonition") }} +
+ {{ section.title|convert_markdown(heading_level, html_id, strip_paragraph=True) }} + {{ section.value.contents|convert_markdown(heading_level, html_id) }} +
diff --git a/docs/_templates/python/material/_base/function.html b/docs/_templates/python/material/_base/function.html index 31ccfc4ba..58a4c37ad 100644 --- a/docs/_templates/python/material/_base/function.html +++ b/docs/_templates/python/material/_base/function.html @@ -29,8 +29,7 @@ {% include "signature.html" with context %} {% endfilter %} {% endif %} - - {% with labels = function.labels %} + {% with labels = function.labels or [(function.parameters._parameters_list and function.parameters._parameters_list[0].name == 'self') and 'method' or 'function'] %} {% include "labels.html" with context %} {% endwith %} @@ -41,7 +40,7 @@ def {% filter format_signature(config.line_length) %} {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} {% include "signature.html" with context %} - {% endfilter %} + {% endfilter %}: {% endfilter %} {% endif %} diff --git a/docs/_templates/python/material/_base/signature.html b/docs/_templates/python/material/_base/signature.html index ae267f5f2..2e1ecd635 100644 --- a/docs/_templates/python/material/_base/signature.html +++ b/docs/_templates/python/material/_base/signature.html @@ -10,14 +10,14 @@ ( {%- for parameter in function.parameters -%} - {%- if parameter.name not in ("self", "cls") or loop.index0 > 0 or not (function.parent and function.parent.is_class) -%} + {%- if 1 -%} {%- if parameter.kind.value == "positional-only" -%} {%- set ns.has_pos_only = True -%} {%- else -%} {%- if ns.has_pos_only and ns.render_pos_only_separator -%} {%- set ns.render_pos_only_separator = False %}/, {% endif -%} - {%- if parameter.kind.value == "keyword-only" -%} + {%- if parameter.kind.value == "keyword-only" -%} {%- if ns.render_kw_only_separator -%} {%- set ns.render_kw_only_separator = False %}*, {% endif -%} {%- endif -%} diff --git a/docs/api/button.md b/docs/api/button.md deleted file mode 100644 index 09e9f40bf..000000000 --- a/docs/api/button.md +++ /dev/null @@ -1,3 +0,0 @@ -::: textual.widgets.Button -::: textual.widgets._button.ButtonVariant -::: textual.widgets._button.InvalidButtonVariant diff --git a/docs/api/checkbox.md b/docs/api/checkbox.md deleted file mode 100644 index 6c9c434f2..000000000 --- a/docs/api/checkbox.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Checkbox diff --git a/docs/api/data_table.md b/docs/api/data_table.md deleted file mode 100644 index c8ac87cde..000000000 --- a/docs/api/data_table.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.DataTable diff --git a/docs/api/directory_tree.md b/docs/api/directory_tree.md deleted file mode 100644 index f9d26e0e0..000000000 --- a/docs/api/directory_tree.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.DirectoryTree diff --git a/docs/api/footer.md b/docs/api/footer.md deleted file mode 100644 index 604c2ef6a..000000000 --- a/docs/api/footer.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Footer diff --git a/docs/api/header.md b/docs/api/header.md deleted file mode 100644 index e6cfc0e44..000000000 --- a/docs/api/header.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Header diff --git a/docs/api/input.md b/docs/api/input.md deleted file mode 100644 index 259ea86f9..000000000 --- a/docs/api/input.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Input diff --git a/docs/api/label.md b/docs/api/label.md deleted file mode 100644 index eee506a2c..000000000 --- a/docs/api/label.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Label diff --git a/docs/api/list_item.md b/docs/api/list_item.md deleted file mode 100644 index f7c4e1185..000000000 --- a/docs/api/list_item.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.ListItem diff --git a/docs/api/list_view.md b/docs/api/list_view.md deleted file mode 100644 index 457ddfc52..000000000 --- a/docs/api/list_view.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.ListView diff --git a/docs/api/loading_indicator.md b/docs/api/loading_indicator.md deleted file mode 100644 index 14b176233..000000000 --- a/docs/api/loading_indicator.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.LoadingIndicator diff --git a/docs/api/logger.md b/docs/api/logger.md new file mode 100644 index 000000000..bd76afcec --- /dev/null +++ b/docs/api/logger.md @@ -0,0 +1 @@ +::: textual.Logger diff --git a/docs/api/markdown.md b/docs/api/markdown.md deleted file mode 100644 index 989a60d80..000000000 --- a/docs/api/markdown.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Markdown diff --git a/docs/api/markdown_viewer.md b/docs/api/markdown_viewer.md deleted file mode 100644 index 7d051d955..000000000 --- a/docs/api/markdown_viewer.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.MarkdownViewer diff --git a/docs/api/message_pump.md b/docs/api/message_pump.md index cb55be8f8..a5aceb775 100644 --- a/docs/api/message_pump.md +++ b/docs/api/message_pump.md @@ -1,5 +1 @@ -A message pump is a class that processes messages. - -It is a base class for the `App`, `Screen`, and `Widget` classes. - ::: textual.message_pump diff --git a/docs/api/option_list.md b/docs/api/option_list.md deleted file mode 100644 index 6c7bc05b5..000000000 --- a/docs/api/option_list.md +++ /dev/null @@ -1,3 +0,0 @@ -::: textual.widgets.OptionList -::: textual.widgets._option_list.Option -::: textual.widgets._option_list.Separator diff --git a/docs/api/placeholder.md b/docs/api/placeholder.md deleted file mode 100644 index ccee041e7..000000000 --- a/docs/api/placeholder.md +++ /dev/null @@ -1,2 +0,0 @@ -::: textual.widgets.Placeholder -::: textual.widgets._placeholder.PlaceholderVariant diff --git a/docs/api/radiobutton.md b/docs/api/radiobutton.md deleted file mode 100644 index f3270a474..000000000 --- a/docs/api/radiobutton.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.RadioButton diff --git a/docs/api/radioset.md b/docs/api/radioset.md deleted file mode 100644 index a661aadef..000000000 --- a/docs/api/radioset.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.RadioSet diff --git a/docs/api/static.md b/docs/api/static.md deleted file mode 100644 index 709b442ef..000000000 --- a/docs/api/static.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Static diff --git a/docs/api/switch.md b/docs/api/switch.md deleted file mode 100644 index 711e817a0..000000000 --- a/docs/api/switch.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Switch diff --git a/docs/api/tabbed_content.md b/docs/api/tabbed_content.md deleted file mode 100644 index 6d4338d21..000000000 --- a/docs/api/tabbed_content.md +++ /dev/null @@ -1,2 +0,0 @@ -::: textual.widgets.TabbedContent -::: textual.widgets.TabPane diff --git a/docs/api/tabs.md b/docs/api/tabs.md deleted file mode 100644 index 6409d4239..000000000 --- a/docs/api/tabs.md +++ /dev/null @@ -1,2 +0,0 @@ -::: textual.widgets.Tab -::: textual.widgets.Tabs diff --git a/docs/api/text_log.md b/docs/api/text_log.md deleted file mode 100644 index 07aa5420d..000000000 --- a/docs/api/text_log.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.TextLog diff --git a/docs/api/toggle_button.md b/docs/api/toggle_button.md deleted file mode 100644 index 74a01db59..000000000 --- a/docs/api/toggle_button.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets._toggle_button.ToggleButton diff --git a/docs/api/tree.md b/docs/api/tree.md deleted file mode 100644 index 2cf2329fa..000000000 --- a/docs/api/tree.md +++ /dev/null @@ -1,4 +0,0 @@ -::: textual.widgets.Tree -::: textual.widgets._tree.TreeNode -::: textual.widgets._tree.NodeID -::: textual.widgets._tree.TreeDataType diff --git a/docs/api/welcome.md b/docs/api/welcome.md deleted file mode 100644 index 5de4507a8..000000000 --- a/docs/api/welcome.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.Welcome diff --git a/docs/api/work.md b/docs/api/work.md index b6bc017bf..539fbfa8e 100644 --- a/docs/api/work.md +++ b/docs/api/work.md @@ -1 +1,2 @@ + ::: textual.work diff --git a/docs/blog/posts/release0-11-0.md b/docs/blog/posts/release0-11-0.md index 600623eeb..ab2ef082e 100644 --- a/docs/blog/posts/release0-11-0.md +++ b/docs/blog/posts/release0-11-0.md @@ -38,7 +38,7 @@ Additionally there are new [update_cell][textual.widgets.DataTable.update_cell] ## Tree control -The [Tree](../../api/tree.md) widget has grown a few methods to programmatically expand, collapse and toggle tree nodes. +The [Tree](../../widgets/tree.md) widget has grown a few methods to programmatically expand, collapse and toggle tree nodes. ## Breaking changes diff --git a/docs/stylesheets/custom.css b/docs/stylesheets/custom.css index 345aec034..ee10d0654 100644 --- a/docs/stylesheets/custom.css +++ b/docs/stylesheets/custom.css @@ -43,9 +43,14 @@ body[data-md-color-primary="black"] .excalidraw svg rect { border: 0; } +.doc-object code { + font-family: "Fira Code", "SFMono-Regular", Consolas, "Courier New", Courier, + monospace; +} + /* Indentation. */ -div.doc-contents:not(.first) .doc-contents { +div.doc-contents:not(.first) { padding-left: 25px; border-left: .05rem solid var(--md-typeset-table-color); } diff --git a/docs/widgets/_template.md b/docs/widgets/_template.md index 1a69a80df..519173aa2 100644 --- a/docs/widgets/_template.md +++ b/docs/widgets/_template.md @@ -61,6 +61,13 @@ The WIDGET widget provides the following component classes: ## See also -- [WIDGET](../api/WIDGET.md) code reference. - Another related API. - Something else useful. + + +--- + + +::: textual.widgets.WIDGET + options: + heading_level: 2 diff --git a/docs/widgets/button.md b/docs/widgets/button.md index 38359d0e8..ef78fb83d 100644 --- a/docs/widgets/button.md +++ b/docs/widgets/button.md @@ -39,12 +39,15 @@ Clicking any of the non-disabled buttons in the example app below will result in ## Messages -### ::: textual.widgets.Button.Pressed +- [Button.Pressed][textual.widgets.Button.Pressed] ## Additional Notes * The spacing between the text and the edges of a button are due to border, _not_ padding. To create a button with zero visible padding, use the `border: none;` declaration. -## See Also +--- -* [Button](../api/button.md) code reference + +::: textual.widgets.Button + options: + heading_level: 2 diff --git a/docs/widgets/checkbox.md b/docs/widgets/checkbox.md index ac4cb2887..57e0c2d21 100644 --- a/docs/widgets/checkbox.md +++ b/docs/widgets/checkbox.md @@ -54,8 +54,12 @@ The checkbox widget provides the following component classes: ## Messages -### ::: textual.widgets.Checkbox.Changed +- [Checkbox.Changed][textual.widgets.Checkbox.Changed] -## See Also -- [Checkbox](../api/checkbox.md) code reference +--- + + +::: textual.widgets.Checkbox + options: + heading_level: 2 diff --git a/docs/widgets/content_switcher.md b/docs/widgets/content_switcher.md index 290392bc0..012e5fa2d 100644 --- a/docs/widgets/content_switcher.md +++ b/docs/widgets/content_switcher.md @@ -46,11 +46,14 @@ When the user presses the "Markdown" button the view is switched: ## Reactive Attributes -| Name | Type | Default | Description | -|-----------|-----------------|---------|----------------------------------------------------------------------| +| Name | Type | Default | Description | +| --------- | --------------- | ------- | ----------------------------------------------------------------------- | | `current` | `str` \| `None` | `None` | The ID of the currently-visible child. `None` means nothing is visible. | -## See Also +--- -* [ContentSwitcher][textual.widgets.ContentSwitcher] code reference + +::: textual.widgets.ContentSwitcher + options: + heading_level: 2 diff --git a/docs/widgets/data_table.md b/docs/widgets/data_table.md index 8c09a032c..d70e43b35 100644 --- a/docs/widgets/data_table.md +++ b/docs/widgets/data_table.md @@ -36,19 +36,13 @@ The example below populates a table with CSV data. ## Messages -### ::: textual.widgets.DataTable.CellHighlighted - -### ::: textual.widgets.DataTable.CellSelected - -### ::: textual.widgets.DataTable.RowHighlighted - -### ::: textual.widgets.DataTable.RowSelected - -### ::: textual.widgets.DataTable.ColumnHighlighted - -### ::: textual.widgets.DataTable.ColumnSelected - -### ::: textual.widgets.DataTable.HeaderSelected +- [DataTable.CellHighlighted][textual.widgets.DataTable.CellHighlighted] +- [DataTable.CellSelected][textual.widgets.DataTable.CellSelected] +- [DataTable.RowHighlighted][textual.widgets.DataTable.RowHighlighted] +- [DataTable.RowSelected][textual.widgets.DataTable.RowSelected] +- [DataTable.ColumnHighlighted][textual.widgets.DataTable.ColumnHighlighted] +- [DataTable.ColumnSelected][textual.widgets.DataTable.ColumnSelected] +- [DataTable.HeaderSelected][textual.widgets.DataTable.HeaderSelected] ## Bindings @@ -68,6 +62,9 @@ The data table widget provides the following component classes: show_root_heading: false show_root_toc_entry: false -## See Also +--- -* [DataTable][textual.widgets.DataTable] code reference + +::: textual.widgets.DataTable + options: + heading_level: 2 diff --git a/docs/widgets/directory_tree.md b/docs/widgets/directory_tree.md index 904319cfe..56f1a0037 100644 --- a/docs/widgets/directory_tree.md +++ b/docs/widgets/directory_tree.md @@ -36,7 +36,7 @@ and directories: ## Messages -### ::: textual.widgets.DirectoryTree.FileSelected +- [DirectoryTree.FileSelected][textual.widgets.DirectoryTree.FileSelected] ## Reactive Attributes @@ -57,5 +57,13 @@ The directory tree widget provides the following component classes: ## See Also -* [DirectoryTree][textual.widgets.DirectoryTree] code reference * [Tree][textual.widgets.Tree] code reference + + + +--- + + +::: textual.widgets.DirectoryTree + options: + heading_level: 2 diff --git a/docs/widgets/footer.md b/docs/widgets/footer.md index d500ba66b..4affbe219 100644 --- a/docs/widgets/footer.md +++ b/docs/widgets/footer.md @@ -46,6 +46,10 @@ The footer widget provides the following component classes: * You can prevent keybindings from appearing in the footer by setting the `show` argument of the `Binding` to `False`. * You can customize the text that appears for the key itself in the footer using the `key_display` argument of `Binding`. -## See Also -* [Footer](../api/footer.md) code reference +--- + + +::: textual.widgets.Footer + options: + heading_level: 2 diff --git a/docs/widgets/header.md b/docs/widgets/header.md index f5dd191e7..3286b3e8c 100644 --- a/docs/widgets/header.md +++ b/docs/widgets/header.md @@ -30,6 +30,9 @@ The example below shows an app with a `Header`. This widget sends no messages. -## See Also +--- -* [Header](../api/header.md) code reference + +::: textual.widgets.Header + options: + heading_level: 2 diff --git a/docs/widgets/input.md b/docs/widgets/input.md index 8a709ff83..6e6d2d6c8 100644 --- a/docs/widgets/input.md +++ b/docs/widgets/input.md @@ -32,9 +32,8 @@ The example below shows how you might create a simple form using two `Input` wid ## Messages -### ::: textual.widgets.Input.Changed - -### ::: textual.widgets.Input.Submitted +- [Input.Changed][textual.widgets.Input.Changed] +- [Input.Submitted][textual.widgets.Input.Submitted] ## Bindings @@ -58,6 +57,9 @@ The input widget provides the following component classes: * The spacing around the text content is due to border. To remove it, set `border: none;` in your CSS. -## See Also +--- -* [Input](../api/input.md) code reference + +::: textual.widgets.Input + options: + heading_level: 2 diff --git a/docs/widgets/label.md b/docs/widgets/label.md index 8445eaae3..ae1216d0a 100644 --- a/docs/widgets/label.md +++ b/docs/widgets/label.md @@ -30,6 +30,9 @@ This widget has no reactive attributes. This widget sends no messages. -## See Also +--- -* [Label](../api/label.md) code reference + +::: textual.widgets.Label + options: + heading_level: 2 diff --git a/docs/widgets/list_item.md b/docs/widgets/list_item.md index 4eaf0263a..309079ea8 100644 --- a/docs/widgets/list_item.md +++ b/docs/widgets/list_item.md @@ -26,16 +26,19 @@ of multiple `ListItem`s. The arrow keys can be used to navigate the list. ## Reactive Attributes | Name | Type | Default | Description | -|---------------|--------|---------|--------------------------------------| +| ------------- | ------ | ------- | ------------------------------------ | | `highlighted` | `bool` | `False` | True if this ListItem is highlighted | #### Attributes | attribute | type | purpose | -|-----------|------------|-----------------------------| +| --------- | ---------- | --------------------------- | | `item` | `ListItem` | The item that was selected. | -## See Also +--- -* [ListItem](../api/list_item.md) code reference + +::: textual.widgets.ListItem + options: + heading_level: 2 diff --git a/docs/widgets/list_view.md b/docs/widgets/list_view.md index 8c38ea027..06ef90585 100644 --- a/docs/widgets/list_view.md +++ b/docs/widgets/list_view.md @@ -37,9 +37,8 @@ The example below shows an app with a simple `ListView`. ## Messages -### ::: textual.widgets.ListView.Highlighted - -### ::: textual.widgets.ListView.Selected +- [ListView.Highlighted][textual.widgets.ListView.Highlighted] +- [ListView.Selected][textual.widgets.ListView.Selected] ## Bindings @@ -50,6 +49,9 @@ The list view widget defines the following bindings: show_root_heading: false show_root_toc_entry: false -## See Also +--- -* [ListView](../api/list_view.md) code reference + +::: textual.widgets.ListView + options: + heading_level: 2 diff --git a/docs/widgets/loading_indicator.md b/docs/widgets/loading_indicator.md index 087e98c31..193611552 100644 --- a/docs/widgets/loading_indicator.md +++ b/docs/widgets/loading_indicator.md @@ -28,7 +28,9 @@ LoadingIndicator { ```python --8<-- "docs/examples/widgets/loading_indicator.py" ``` +--- -## See Also -* [LoadingIndicator](../api/loading_indicator.md) code reference +::: textual.widgets.LoadingIndicator + options: + heading_level: 2 diff --git a/docs/widgets/markdown.md b/docs/widgets/markdown.md index f06c01ee8..6897c4c71 100644 --- a/docs/widgets/markdown.md +++ b/docs/widgets/markdown.md @@ -29,14 +29,20 @@ The following example displays Markdown from a string. ## Messages -### ::: textual.widgets.Markdown.TableOfContentsUpdated - -### ::: textual.widgets.Markdown.TableOfContentsSelected - -### ::: textual.widgets.Markdown.LinkClicked +- [Markdown.TableOfContentsUpdated][textual.widgets.Markdown.TableOfContentsUpdated] +- [Markdown.TableOfContentsSelected][textual.widgets.Markdown.TableOfContentsSelected] +- [Markdown.LinkClicked][textual.widgets.Markdown.LinkClicked] ## See Also -* [Markdown][textual.widgets.Markdown] code reference + * [MarkdownViewer][textual.widgets.MarkdownViewer] code reference + + +--- + + +::: textual.widgets.Markdown + options: + heading_level: 2 diff --git a/docs/widgets/markdown_viewer.md b/docs/widgets/markdown_viewer.md index 5a0dd2f2e..6a4e3f47d 100644 --- a/docs/widgets/markdown_viewer.md +++ b/docs/widgets/markdown_viewer.md @@ -35,5 +35,13 @@ The following example displays Markdown from a string and a Table of Contents. ## See Also -* [MarkdownViewer][textual.widgets.MarkdownViewer] code reference * [Markdown][textual.widgets.Markdown] code reference + + + +--- + + +::: textual.widgets.MarkdownViewer + options: + heading_level: 2 diff --git a/docs/widgets/option_list.md b/docs/widgets/option_list.md index ab6cfca45..2c4a80789 100644 --- a/docs/widgets/option_list.md +++ b/docs/widgets/option_list.md @@ -82,14 +82,13 @@ tables](https://rich.readthedocs.io/en/latest/tables.html): ## Reactive Attributes | Name | Type | Default | Description | -|---------------|-----------------|---------|---------------------------------------------------------------------------| +| ------------- | --------------- | ------- | ------------------------------------------------------------------------- | | `highlighted` | `int` \| `None` | `None` | The index of the highlighted option. `None` means nothing is highlighted. | ## Messages -### ::: textual.widgets.OptionList.OptionHighlighted - -### ::: textual.widgets.OptionList.OptionSelected +- [OptionList.OptionHighlight][textual.widgets.OptionList.OptionHighlighted] +- [OptionList.OptionSelected][textual.widgets.OptionList.OptionSelected] Both of the messages above inherit from this common base, which makes available the following properties relating to the `OptionList` and the @@ -122,6 +121,8 @@ The option list provides the following component classes: show_root_heading: false show_root_toc_entry: false -## See Also -* [OptionList][textual.widgets.OptionList] code reference + +::: textual.widgets.OptionList + options: + heading_level: 2 diff --git a/docs/widgets/placeholder.md b/docs/widgets/placeholder.md index e9a07b28e..2dbdbd658 100644 --- a/docs/widgets/placeholder.md +++ b/docs/widgets/placeholder.md @@ -34,15 +34,18 @@ The example below shows each placeholder variant. ## Reactive Attributes -| Name | Type | Default | Description | -| ---------- | ------ | ----------- | -------------------------------------------------- | -| `variant` | `str` | `"default"` | Styling variant. One of `default`, `size`, `text`. | +| Name | Type | Default | Description | +| --------- | ----- | ----------- | -------------------------------------------------- | +| `variant` | `str` | `"default"` | Styling variant. One of `default`, `size`, `text`. | ## Messages This widget sends no messages. -## See Also +--- -* [Placeholder](../api/placeholder.md) code reference + +::: textual.widgets.Placeholder + options: + heading_level: 2 diff --git a/docs/widgets/radiobutton.md b/docs/widgets/radiobutton.md index d3f93a7ef..c23fa44b7 100644 --- a/docs/widgets/radiobutton.md +++ b/docs/widgets/radiobutton.md @@ -56,9 +56,15 @@ The radio button widget provides the following component classes: ## Messages -### ::: textual.widgets.RadioButton.Changed +- [RadioButton.Changed][textual.widgets.RadioButton.Changed] ## See Also -- [RadioButton](../api/radiobutton.md) code reference - [RadioSet](./radioset.md) + +--- + + +::: textual.widgets.RadioButton + options: + heading_level: 2 diff --git a/docs/widgets/radioset.md b/docs/widgets/radioset.md index 414b35e36..1aa632d9b 100644 --- a/docs/widgets/radioset.md +++ b/docs/widgets/radioset.md @@ -31,7 +31,7 @@ The example below shows two radio sets, one built using a collection of ## Messages -### ::: textual.widgets.RadioSet.Changed +- [RadioSet.Changed][textual.widgets.RadioSet.Changed] #### Example @@ -56,5 +56,12 @@ Here is an example of using the message to react to changes in a `RadioSet`: ## See Also -- [RadioSet](../api/radioset.md) code reference + - [RadioButton](./radiobutton.md) + +--- + + +::: textual.widgets.RadioSet + options: + heading_level: 2 diff --git a/docs/widgets/static.md b/docs/widgets/static.md index 9c01d6c1c..d8ab1bb99 100644 --- a/docs/widgets/static.md +++ b/docs/widgets/static.md @@ -31,5 +31,12 @@ This widget sends no messages. ## See Also -* [Static](../api/static.md) code reference * [Label](./label.md) + + +--- + + +::: textual.widgets.Static + options: + heading_level: 2 diff --git a/docs/widgets/switch.md b/docs/widgets/switch.md index e5f3bcd7c..e228d8f90 100644 --- a/docs/widgets/switch.md +++ b/docs/widgets/switch.md @@ -52,12 +52,15 @@ The switch widget provides the following component classes: ## Messages -### ::: textual.widgets.Switch.Changed +- [Switch.Changed][textual.widgets.Switch.Changed] ## Additional Notes - To remove the spacing around a `Switch`, set `border: none;` and `padding: 0;`. -## See Also +--- -- [Switch](../api/switch.md) code reference + +::: textual.widgets.Switch + options: + heading_level: 2 diff --git a/docs/widgets/tabbed_content.md b/docs/widgets/tabbed_content.md index ab723c097..73667e023 100644 --- a/docs/widgets/tabbed_content.md +++ b/docs/widgets/tabbed_content.md @@ -103,6 +103,22 @@ The following example contains a `TabbedContent` with three tabs. ## See also -- [TabbedContent](../api/tabbed_content.md) code reference. -- [Tabs](../api/tabs.md) code reference. -- [ContentSwitcher](../api/content_switcher.md) code reference. + +- [Tabs](tabs.md) +- [ContentSwitcher](content_switcher.md) + + +--- + + +::: textual.widgets.TabbedContent + options: + heading_level: 2 + + +--- + + +::: textual.widgets.TabPane + options: + heading_level: 2 diff --git a/docs/widgets/tabs.md b/docs/widgets/tabs.md index 759fc3e76..a77e31755 100644 --- a/docs/widgets/tabs.md +++ b/docs/widgets/tabs.md @@ -61,8 +61,8 @@ The following example adds a `Tabs` widget above a text label. Press ++a++ to ad ## Messages -### ::: textual.widgets.Tabs.TabActivated -### ::: textual.widgets.Tabs.Cleared +- [Tabs.TabActivate][textual.widgets.Tabs.TabActivated] +- [Tabs.Cleared][textual.widgets.Tabs.Cleared] ## Bindings @@ -73,6 +73,17 @@ The Tabs widget defines the following bindings: show_root_heading: false show_root_toc_entry: false -## See Also -- [Tabs](../api/tabs.md) code reference +--- + + +::: textual.widgets.Tabs + options: + heading_level: 2 + + +--- + +::: textual.widgets.Tab + options: + heading_level: 2 diff --git a/docs/widgets/text_log.md b/docs/widgets/text_log.md index d354fc24b..bb13c426f 100644 --- a/docs/widgets/text_log.md +++ b/docs/widgets/text_log.md @@ -39,6 +39,9 @@ The example below shows an application showing a `TextLog` with different kinds This widget sends no messages. -## See Also +--- -* [TextLog](../api/text_log.md) code reference + +::: textual.widgets.TextLog + options: + heading_level: 2 diff --git a/docs/widgets/tree.md b/docs/widgets/tree.md index 153fff4c3..2f6cb000f 100644 --- a/docs/widgets/tree.md +++ b/docs/widgets/tree.md @@ -62,7 +62,16 @@ The tree widget provides the following component classes: show_root_heading: false show_root_toc_entry: false -## See Also -* [Tree][textual.widgets.Tree] code reference -* [TreeNode][textual.widgets.tree.TreeNode] code reference +--- + + +::: textual.widgets.Tree + options: + heading_level: 2 + +--- + +::: textual.widgets.tree.TreeNode + options: + heading_level: 2 diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index b0c72f150..4d2882a1b 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -152,50 +152,25 @@ nav: - "api/app.md" - "api/await_remove.md" - "api/binding.md" - - "api/button.md" - - "api/checkbox.md" - "api/color.md" - "api/containers.md" - - "api/content_switcher.md" - "api/coordinate.md" - - "api/data_table.md" - - "api/directory_tree.md" - "api/dom_node.md" - "api/events.md" - - "api/footer.md" - "api/geometry.md" - - "api/header.md" - "api/index.md" - - "api/input.md" - - "api/label.md" - - "api/list_item.md" - - "api/list_view.md" - - "api/loading_indicator.md" + - "api/logger.md" - "api/logging.md" - - "api/markdown_viewer.md" - - "api/markdown.md" - "api/message_pump.md" - "api/message.md" - - "api/option_list.md" - "api/pilot.md" - - "api/placeholder.md" - "api/query.md" - - "api/radiobutton.md" - - "api/radioset.md" - "api/reactive.md" - "api/screen.md" - "api/scroll_view.md" - - "api/static.md" - "api/strip.md" - - "api/switch.md" - - "api/tabbed_content.md" - - "api/tabs.md" - - "api/text_log.md" - "api/timer.md" - - "api/toggle_button.md" - - "api/tree.md" - "api/walk.md" - - "api/welcome.md" - "api/widget.md" - "api/work.md" - "api/worker.md" diff --git a/src/textual/__init__.py b/src/textual/__init__.py index e23bdf6ee..c009ca3ab 100644 --- a/src/textual/__init__.py +++ b/src/textual/__init__.py @@ -1,7 +1,6 @@ from __future__ import annotations import inspect -from functools import partial, wraps from typing import TYPE_CHECKING, Callable import rich.repr diff --git a/src/textual/_animator.py b/src/textual/_animator.py index fa8c1fb64..f27966a19 100644 --- a/src/textual/_animator.py +++ b/src/textual/_animator.py @@ -139,11 +139,11 @@ class BoundAnimator: attribute: Name of the attribute to animate. value: The value to animate to. final_value: The final value of the animation. Defaults to `value` if not set. - duration: The duration of the animate. Defaults to None. - speed: The speed of the animation. Defaults to None. - delay: A delay (in seconds) before the animation starts. Defaults to 0.0. - easing: An easing method. Defaults to "in_out_cubic". - on_complete: A callable to invoke when the animation is finished. Defaults to None. + duration: The duration of the animate. + speed: The speed of the animation. + delay: A delay (in seconds) before the animation starts. + easing: An easing method. + on_complete: A callable to invoke when the animation is finished. """ start_value = getattr(self._obj, attribute) @@ -237,11 +237,11 @@ class Animator: obj: The object containing the attribute. attribute: The name of the attribute. value: The destination value of the attribute. - final_value: The final value, or ellipsis if it is the same as ``value``. Defaults to Ellipsis/ - duration: The duration of the animation, or ``None`` to use speed. Defaults to ``None``. - speed: The speed of the animation. Defaults to None. - easing: An easing function. Defaults to DEFAULT_EASING. - delay: Number of seconds to delay the start of the animation by. Defaults to 0. + final_value: The final value, or ellipsis if it is the same as ``value``. + duration: The duration of the animation, or ``None`` to use speed. + speed: The speed of the animation. + easing: An easing function. + delay: Number of seconds to delay the start of the animation by. on_complete: Callback to run after the animation completes. """ animate_callback = partial( @@ -280,10 +280,10 @@ class Animator: obj: The object containing the attribute. attribute: The name of the attribute. value: The destination value of the attribute. - final_value: The final value, or ellipsis if it is the same as ``value``. Defaults to .... - duration: The duration of the animation, or ``None`` to use speed. Defaults to ``None``. - speed: The speed of the animation. Defaults to None. - easing: An easing function. Defaults to DEFAULT_EASING. + final_value: The final value, or ellipsis if it is the same as ``value``. + duration: The duration of the animation, or ``None`` to use speed. + speed: The speed of the animation. + easing: An easing function. on_complete: Callback to run after the animation completes. """ if not hasattr(obj, attribute): diff --git a/src/textual/_cache.py b/src/textual/_cache.py index c4b691ca5..b808a97a0 100644 --- a/src/textual/_cache.py +++ b/src/textual/_cache.py @@ -142,7 +142,7 @@ class LRUCache(Generic[CacheKey, CacheValue]): Args: key: Key - default: Default to return if key is not present. Defaults to None. + default: Default to return if key is not present. Returns: Either the value or a default. @@ -256,7 +256,7 @@ class FIFOCache(Generic[CacheKey, CacheValue]): Args: key: Key - default: Default to return if key is not present. Defaults to None. + default: Default to return if key is not present. Returns: Either the value or a default. diff --git a/src/textual/_work_decorator.py b/src/textual/_work_decorator.py index 47ca4409b..97c5eb714 100644 --- a/src/textual/_work_decorator.py +++ b/src/textual/_work_decorator.py @@ -1,3 +1,10 @@ +""" + +A decorator used to create [workers](/guide/workers). + +""" + + from __future__ import annotations from functools import partial, wraps @@ -53,7 +60,7 @@ def work( exit_on_error: bool = True, exclusive: bool = False, ) -> Callable[FactoryParamSpec, Worker[ReturnType]] | Decorator: - """Worker decorator factory. + """A decorator used to create [workers](/guide/workers). Args: method: A function or coroutine. diff --git a/src/textual/_worker_manager.py b/src/textual/_worker_manager.py index 8d66c0a28..1508ef5b7 100644 --- a/src/textual/_worker_manager.py +++ b/src/textual/_worker_manager.py @@ -1,3 +1,11 @@ +""" +A class to manage [workers](/guide/workers) for an app. + +You access this object via [App.workers][textual.app.App.workers] or [Widget.workers][textual.dom.DOMNode.workers]. + +""" + + from __future__ import annotations import asyncio diff --git a/src/textual/app.py b/src/textual/app.py index e1c5bd93c..10b795c7a 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -66,7 +66,7 @@ from ._wait import wait_for_idle from ._worker_manager import WorkerManager from .actions import ActionParseResult, SkipAction from .await_remove import AwaitRemove -from .binding import Binding, Bindings +from .binding import Binding, _Bindings from .css.query import NoMatches from .css.stylesheet import Stylesheet from .design import ColorSystem @@ -242,10 +242,10 @@ CallThreadReturnType = TypeVar("CallThreadReturnType") class App(Generic[ReturnType], DOMNode): """The base class for Textual Applications. Args: - driver_class: Driver class or ``None`` to auto-detect. Defaults to None. + driver_class: Driver class or ``None`` to auto-detect. css_path: Path to CSS or ``None`` for no CSS file. - Defaults to None. To load multiple CSS files, pass a list of strings or paths which will be loaded in order. - watch_css: Watch CSS for changes. Defaults to False. + To load multiple CSS files, pass a list of strings or paths which will be loaded in order. + watch_css: Watch CSS for changes. Raises: CssPathError: When the supplied CSS path(s) are an unexpected type. @@ -436,7 +436,12 @@ class App(Generic[ReturnType], DOMNode): @property def workers(self) -> WorkerManager: - """A worker manager.""" + """The worker manager. + + Returns: + An object to manage workers. + + """ return self._workers @property @@ -446,7 +451,12 @@ class App(Generic[ReturnType], DOMNode): @property def children(self) -> Sequence["Widget"]: - """A view on to the children which contains just the screen.""" + """A view on to the App's children (just the screen). + + Returns: + A sequence of widgets. + + """ try: return (self.screen,) except ScreenError: @@ -489,12 +499,12 @@ class App(Generic[ReturnType], DOMNode): Args: attribute: Name of the attribute to animate. value: The value to animate to. - final_value: The final value of the animation. Defaults to `value` if not set. - duration: The duration of the animate. Defaults to None. - speed: The speed of the animation. Defaults to None. - delay: A delay (in seconds) before the animation starts. Defaults to 0.0. - easing: An easing method. Defaults to "in_out_cubic". - on_complete: A callable to invoke when the animation is finished. Defaults to None. + final_value: The final value of the animation. + duration: The duration of the animate. + speed: The speed of the animation. + delay: A delay (in seconds) before the animation starts. + easing: An easing method. + on_complete: A callable to invoke when the animation is finished. """ self._animate( @@ -520,7 +530,12 @@ class App(Generic[ReturnType], DOMNode): @property def screen_stack(self) -> list[Screen]: - """A *copy* of the screen stack.""" + """A *copy* of the screen stack. + + Returns: + A snapshot of the current state of the screen stack. + + """ return self._screen_stack.copy() def exit( @@ -529,7 +544,7 @@ class App(Generic[ReturnType], DOMNode): """Exit the app, and return the supplied result. Args: - result: Return value. Defaults to None. + result: Return value. message: Optional message to display on exit. """ self._exit = True @@ -540,7 +555,12 @@ class App(Generic[ReturnType], DOMNode): @property def focused(self) -> Widget | None: - """The widget that is focused on the currently active screen.""" + """The widget that is focused on the currently active screen. + + Returns: + The currently focused widget, or `None` if nothing is focused. + + """ return self.screen.focused @property @@ -666,7 +686,12 @@ class App(Generic[ReturnType], DOMNode): @property def size(self) -> Size: - """The size of the terminal.""" + """The size of the terminal. + + Returns: + Size of the terminal. + + """ if self._driver is not None and self._driver._size is not None: width, height = self._driver._size else: @@ -675,7 +700,12 @@ class App(Generic[ReturnType], DOMNode): @property def log(self) -> Logger: - """The logger object.""" + """Textual log interface. + + Returns: + A Textual logger. + + """ return self._logger def _log( @@ -699,7 +729,7 @@ class App(Generic[ReturnType], DOMNode): ``` Args: - verbosity: Verbosity level 0-3. Defaults to 1. + verbosity: Verbosity level 0-3. """ devtools = self.devtools @@ -786,7 +816,7 @@ class App(Generic[ReturnType], DOMNode): """Save an SVG "screenshot". This action will save an SVG file containing the current contents of the screen. Args: - filename: Filename of screenshot, or None to auto-generate. Defaults to None. + filename: Filename of screenshot, or None to auto-generate. path: Path to directory. Defaults to current working directory. """ self.save_screenshot(filename, path) @@ -796,7 +826,7 @@ class App(Generic[ReturnType], DOMNode): Args: title: The title of the exported screenshot or None - to use app title. Defaults to None. + to use app title. """ assert self._driver is not None, "App must be running" @@ -826,7 +856,7 @@ class App(Generic[ReturnType], DOMNode): Args: filename: Filename of SVG screenshot, or None to auto-generate - a filename with the date and time. Defaults to None. + a filename with the date and time. path: Path to directory for output. Defaults to current working directory. time_format: Date and time format to use if filename is None. Defaults to a format like ISO 8601 with some reserved characters replaced with underscores. @@ -865,9 +895,9 @@ class App(Generic[ReturnType], DOMNode): Args: keys: A comma separated list of keys, i.e. action: Action to bind to. - description: Short description of action. Defaults to "". - show: Show key in UI. Defaults to True. - key_display: Replacement text for key, or None to use default. Defaults to None. + description: Short description of action. + show: Show key in UI. + key_display: Replacement text for key, or None to use default. """ self._bindings.bind( keys, action, description, show=show, key_display=key_display @@ -930,9 +960,9 @@ class App(Generic[ReturnType], DOMNode): """An asynchronous context manager for testing app. Args: - headless: Run in headless mode (no output or input). Defaults to True. + headless: Run in headless mode (no output or input). size: Force terminal size to `(WIDTH, HEIGHT)`, - or None to auto-detect. Defaults to None. + or None to auto-detect. """ from .pilot import Pilot @@ -981,9 +1011,9 @@ class App(Generic[ReturnType], DOMNode): """Run the app asynchronously. Args: - headless: Run in headless mode (no output). Defaults to False. + headless: Run in headless mode (no output). size: Force terminal size to `(WIDTH, HEIGHT)`, - or None to auto-detect. Defaults to None. + or None to auto-detect. auto_pilot: An auto pilot coroutine. Returns: @@ -1042,9 +1072,9 @@ class App(Generic[ReturnType], DOMNode): """Run the app. Args: - headless: Run in headless mode (no output). Defaults to False. + headless: Run in headless mode (no output). size: Force terminal size to `(WIDTH, HEIGHT)`, - or None to auto-detect. Defaults to None. + or None to auto-detect. auto_pilot: An auto pilot coroutine. Returns: @@ -1119,7 +1149,6 @@ class App(Generic[ReturnType], DOMNode): Args: id: The ID of the node to search for. expect_type: Require the object be of the supplied type, or None for any type. - Defaults to None. Returns: The first child of this node with the specified ID. @@ -1918,7 +1947,7 @@ class App(Generic[ReturnType], DOMNode): """Refresh CSS. Args: - animate: Also execute CSS animations. Defaults to True. + animate: Also execute CSS animations. """ stylesheet = self.app.stylesheet stylesheet.set_variables(self.get_css_variables()) @@ -1973,14 +2002,14 @@ class App(Generic[ReturnType], DOMNode): self.console.bell() @property - def _binding_chain(self) -> list[tuple[DOMNode, Bindings]]: + def _binding_chain(self) -> list[tuple[DOMNode, _Bindings]]: """Get a chain of nodes and bindings to consider. If no widget is focused, returns the bindings from both the screen and the app level bindings. Otherwise, combines all the bindings from the currently focused node up the DOM to the root App. """ focused = self.focused - namespace_bindings: list[tuple[DOMNode, Bindings]] + namespace_bindings: list[tuple[DOMNode, _Bindings]] if focused is None: namespace_bindings = [ @@ -1995,7 +2024,7 @@ class App(Generic[ReturnType], DOMNode): return namespace_bindings @property - def _modal_binding_chain(self) -> list[tuple[DOMNode, Bindings]]: + def _modal_binding_chain(self) -> list[tuple[DOMNode, _Bindings]]: """The binding chain, ignoring everything before the last modal.""" binding_chain = self._binding_chain for index, (node, _bindings) in enumerate(binding_chain, 1): @@ -2061,7 +2090,7 @@ class App(Generic[ReturnType], DOMNode): Args: action: Action encoded in a string. default_namespace: Namespace to use if not provided in the action, - or None to use app. Defaults to None. + or None to use app. Returns: True if the event has handled. diff --git a/src/textual/await_remove.py b/src/textual/await_remove.py index 602acb27d..c879a289d 100644 --- a/src/textual/await_remove.py +++ b/src/textual/await_remove.py @@ -1,4 +1,9 @@ -"""Provides the type of an awaitable remove.""" +""" + +An *optionally* awaitable object returned by methods that remove widgets. + + +""" from asyncio import Event, Task from typing import Generator diff --git a/src/textual/binding.py b/src/textual/binding.py index 107bc9d45..4873b5211 100644 --- a/src/textual/binding.py +++ b/src/textual/binding.py @@ -1,3 +1,12 @@ +""" + +A binding maps a key press on to an action. + +See [bindings](/guide/input#bindings) in the guide for details. + +""" + + from __future__ import annotations from dataclasses import dataclass @@ -44,7 +53,7 @@ class Binding: @rich.repr.auto -class Bindings: +class _Bindings: """Manage a set of bindings.""" def __init__( @@ -98,13 +107,13 @@ class Bindings: else {} ) - def copy(self) -> Bindings: + def copy(self) -> _Bindings: """Return a copy of this instance. Return: New bindings object. """ - copy = Bindings() + copy = _Bindings() copy.keys = self.keys.copy() return copy @@ -112,7 +121,7 @@ class Bindings: yield self.keys @classmethod - def merge(cls, bindings: Iterable[Bindings]) -> Bindings: + def merge(cls, bindings: Iterable[_Bindings]) -> _Bindings: """Merge a bindings. Subsequent bound keys override initial keys. Args: @@ -124,7 +133,7 @@ class Bindings: keys: dict[str, Binding] = {} for _bindings in bindings: keys.update(_bindings.keys) - return Bindings(keys.values()) + return _Bindings(keys.values()) @property def shown_keys(self) -> list[Binding]: diff --git a/src/textual/color.py b/src/textual/color.py index a43f073d4..1412bfba9 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -1,9 +1,6 @@ """ This module contains a powerful Color class which Textual uses to expose colors. -The only exception would be for Rich renderables, which require a rich.color.Color instance. -You can convert from a Textual color to a Rich color with the [rich_color][textual.color.Color.rich_color] property. - ## Named colors The following named colors are used by the [parse][textual.color.Color.parse] method. @@ -46,6 +43,7 @@ from rich.color import ColorType from rich.color_triplet import ColorTriplet from rich.style import Style from rich.text import Text +from typing_extensions import Final from textual.css.scalar import percentage_string_to_float from textual.css.tokenize import CLOSE_BRACE, COMMA, DECIMAL, OPEN_BRACE, PERCENT @@ -58,14 +56,14 @@ _TRUECOLOR = ColorType.TRUECOLOR class HSL(NamedTuple): - """A color in HLS format.""" + """A color in HLS (Hue, Saturation, Lightness) format.""" h: float - """Hue""" + """Hue in range 0 to 1.""" s: float - """Saturation""" + """Saturation in range 0 to 1.""" l: float - """Lightness""" + """Lightness in range 0 to 1.""" @property def css(self) -> str: @@ -73,28 +71,32 @@ class HSL(NamedTuple): h, s, l = self def as_str(number: float) -> str: + """Format a float.""" return f"{number:.1f}".rstrip("0").rstrip(".") return f"hsl({as_str(h*360)},{as_str(s*100)}%,{as_str(l*100)}%)" class HSV(NamedTuple): - """A color in HSV format.""" + """A color in HSV (Hue, Saturation, Value) format.""" h: float - """Hue""" + """Hue in range 0 to 1.""" s: float - """Saturation""" + """Saturation in range 0 to 1""" v: float - """Value""" + """Value un range 0 to 1.""" class Lab(NamedTuple): """A color in CIE-L*ab format.""" L: float + """Lightness in range 0 to 100.""" a: float + """A axis in range -127 to 128.""" b: float + """B axis in range -127 to 128.""" RE_COLOR = re.compile( @@ -126,7 +128,7 @@ class ColorParseError(Exception): Args: message: The error message - suggested_color: A close color we can suggest. Defaults to None. + suggested_color: A close color we can suggest. """ def __init__(self, message: str, suggested_color: str | None = None): @@ -136,26 +138,32 @@ class ColorParseError(Exception): @rich.repr.auto class Color(NamedTuple): - """A class to represent a RGB color with an alpha component.""" + """A class to represent a color. + + Colors are stored as three values representing the degree of red, green, and blue in a color, and a + fourth "alpha" value which defines where the color lies on a gradient of opaque to transparent. + + + """ r: int - """Red component (0-255)""" + """Red component in range 0 to 255.""" g: int - """Green component (0-255)""" + """Green component in range 0 to 255.""" b: int - """Blue component (0-255)""" + """Blue component in range 0 to 255.""" a: float = 1.0 - """Alpha component (0-1)""" + """Alpha component in range 0 to 1.""" @classmethod def from_rich_color(cls, rich_color: RichColor) -> Color: """Create a new color from Rich's Color class. Args: - rich_color: An instance of rich.color.Color. + rich_color: An instance of [Rich color][rich.color.Color]. Returns: - A new Color. + A new Color instance. """ r, g, b = rich_color.get_truecolor() return cls(r, g, b) @@ -192,22 +200,12 @@ class Color(NamedTuple): @property def is_transparent(self) -> bool: - """Check if the color is transparent, i.e. has 0 alpha. - - Returns: - True if transparent, otherwise False. - - """ + """Is the color transparent (i.e. has 0 alpha)?""" return self.a == 0 @property def clamped(self) -> Color: - """Get a color with all components saturated to maximum and minimum values. - - Returns: - A color object. - - """ + """A clamped color (this color with all values in expected range).""" r, g, b, a = self _clamp = clamp color = Color( @@ -243,20 +241,16 @@ class Color(NamedTuple): @property def rgb(self) -> tuple[int, int, int]: - """Get just the red, green, and blue components. - - Returns: - Color components - """ + """A tuple of the red, gree, and blue color components.""" r, g, b, _ = self return (r, g, b) @property def hsl(self) -> HSL: - """Get the color as HSL. + """This color in HSL format. + + HSL color is an alternative way of representing a color, which can be used in certain color calculations. - Returns: - Color in HSL format. """ r, g, b = self.normalized h, l, s = rgb_to_hls(r, g, b) @@ -264,10 +258,10 @@ class Color(NamedTuple): @property def brightness(self) -> float: - """Get the human perceptual brightness. + """The human perceptual brightness. - Returns: - Brightness value (0-1). + A value of 1 is returned for pure white, and 0 for pure black. + Other colors lie on a gradient between the two extremes. """ r, g, b = self.normalized @@ -278,8 +272,7 @@ class Color(NamedTuple): def hex(self) -> str: """The color in CSS hex form, with 6 digits for RGB, and 8 digits for RGBA. - Returns: - A CSS hex-style color, e.g. `"#46b3de"` or `"#3342457f"` + For example, `"#46b3de"` for an RGB color, or `"#3342457f"` for a color with alpha. """ r, g, b, a = self.clamped @@ -293,8 +286,7 @@ class Color(NamedTuple): def hex6(self) -> str: """The color in CSS hex form, with 6 digits for RGB. Alpha is ignored. - Returns: - A CSS hex-style color, e.g. "#46b3de" + For example, `"#46b3de"`. """ r, g, b, _a = self.clamped @@ -302,10 +294,9 @@ class Color(NamedTuple): @property def css(self) -> str: - """The color in CSS rgb or rgba form. + """The color in CSS RGB or RGBA form. - Returns: - A CSS style color, e.g. `"rgb(10,20,30)"` or `"rgb(50,70,80,0.5)"` + For example, `"rgb(10,20,30)"` for an RGB color, or `"rgb(50,70,80,0.5)"` for an RGBA color. """ r, g, b, a = self @@ -313,11 +304,7 @@ class Color(NamedTuple): @property def monochrome(self) -> Color: - """Get a monochrome version of this color. - - Returns: - A new monochrome color. - """ + """A monochrome version of this color.""" r, g, b, a = self gray = round(r * 0.2126 + g * 0.7152 + b * 0.0722) return Color(gray, gray, gray, a) @@ -358,10 +345,14 @@ class Color(NamedTuple): ) -> Color: """Generate a new color between two colors. + This method calculates a new color on a gradient. + The position on the gradient is given by `factor`, which is a float between 0 and 1, where 0 is the original color, and 1 is the `destination` color. + A value of `gradient` between the two extremes produces a color somewhere between the two end points. + Args: destination: Another color. factor: A blend factor, 0 -> 1. - alpha: New alpha for result. Defaults to None. + alpha: New alpha for result. Returns: A new color. @@ -515,7 +506,7 @@ class Color(NamedTuple): Args: amount: Value between 0-1 to reduce luminance by. - alpha: Alpha component for new color or None to copy alpha. Defaults to None. + alpha: Alpha component for new color or None to copy alpha. Returns: New color. @@ -529,7 +520,7 @@ class Color(NamedTuple): Args: amount: Value between 0-1 to increase luminance by. - alpha: Alpha component for new color or None to copy alpha. Defaults to None. + alpha: Alpha component for new color or None to copy alpha. Returns: New color. @@ -541,8 +532,7 @@ class Color(NamedTuple): """Get a light or dark color that best contrasts this color, for use with text. Args: - alpha: An alpha value to adjust the pure white / black by. - Defaults to 0.95. + alpha: An alpha value to apply to the result. Returns: A new color, either an off-white or off-black @@ -597,8 +587,10 @@ class Gradient: # Color constants -WHITE = Color(255, 255, 255) -BLACK = Color(0, 0, 0) +WHITE: Final = Color(255, 255, 255) +"""A constant for pure white.""" +BLACK: Final = Color(0, 0, 0) +"""A constant for pure black.""" def rgb_to_lab(rgb: Color) -> Lab: diff --git a/src/textual/containers.py b/src/textual/containers.py index a2ddbf422..99605ace7 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -1,3 +1,9 @@ +""" +Container widgets for quick styling. + +""" + + from .widget import Widget diff --git a/src/textual/coordinate.py b/src/textual/coordinate.py index e53590ef8..f36014ff6 100644 --- a/src/textual/coordinate.py +++ b/src/textual/coordinate.py @@ -1,3 +1,8 @@ +""" +A class to store a coordinate, used by the [DataTable][textual.widgets.DataTable]. + +""" + from __future__ import annotations from typing import NamedTuple diff --git a/src/textual/css/_error_tools.py b/src/textual/css/_error_tools.py index db01f4bf3..a5e0972e0 100644 --- a/src/textual/css/_error_tools.py +++ b/src/textual/css/_error_tools.py @@ -13,7 +13,7 @@ def friendly_list( Args: words: A list of words. - joiner: The last joiner word. Defaults to "or". + joiner: The last joiner word. Returns: List as prose. diff --git a/src/textual/css/_help_renderables.py b/src/textual/css/_help_renderables.py index 939a5933a..bce6a9a60 100644 --- a/src/textual/css/_help_renderables.py +++ b/src/textual/css/_help_renderables.py @@ -74,7 +74,7 @@ class HelpText: Attributes: summary: A succinct summary of the issue. bullets: Bullet points which provide additional - context around the issue. These are rendered below the summary. Defaults to None. + context around the issue. These are rendered below the summary. """ def __init__( diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index ef98fb77e..eb8546234 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -68,7 +68,7 @@ def _join_tokens(tokens: Iterable[Token], joiner: str = "") -> str: Args: tokens: Tokens to join - joiner: String to join on, defaults to "" + joiner: String to join on. Returns: The tokens, joined together to form a string. diff --git a/src/textual/css/query.py b/src/textual/css/query.py index 98e0063a0..77141ece3 100644 --- a/src/textual/css/query.py +++ b/src/textual/css/query.py @@ -72,6 +72,21 @@ class DOMQuery(Generic[QueryType]): exclude: str | None = None, parent: DOMQuery | None = None, ) -> None: + """Initialize a query object. + + !!! warning + + You won't need to construct this manually, as `DOMQuery` objects are returned by [query][textual.dom.DOMNode.query]. + + Args: + node: A DOM node. + filter: Query to filter children in the node. + exclude: Query to exclude children in the node. + parent: The parent query, if this is the result of filtering another query. + + Raises: + InvalidQueryFormat: If the format of the query is invalid. + """ self._node = node self._nodes: list[QueryType] | None = None self._filters: list[tuple[SelectorSet, ...]] = ( @@ -95,6 +110,7 @@ class DOMQuery(Generic[QueryType]): @property def node(self) -> DOMNode: + """The node being queried.""" return self._node @property @@ -193,7 +209,7 @@ class DOMQuery(Generic[QueryType]): Args: expect_type: Require matched node is of this type, - or None for any type. Defaults to None. + or None for any type. Raises: WrongType: If the wrong type was found. @@ -228,7 +244,7 @@ class DOMQuery(Generic[QueryType]): Args: expect_type: Require matched node is of this type, - or None for any type. Defaults to None. + or None for any type. Raises: WrongType: If the wrong type was found. @@ -272,7 +288,7 @@ class DOMQuery(Generic[QueryType]): Args: expect_type: Require matched node is of this type, - or None for any type. Defaults to None. + or None for any type. Raises: WrongType: If the wrong type was found. @@ -305,7 +321,7 @@ class DOMQuery(Generic[QueryType]): Args: filter_type: A Widget class to filter results, - or None for no filter. Defaults to None. + or None for no filter. Yields: Iterator[Widget | ExpectType]: An iterator of Widget instances. @@ -383,7 +399,7 @@ class DOMQuery(Generic[QueryType]): """Set styles on matched nodes. Args: - css: CSS declarations to parser, or None. Defaults to None. + css: CSS declarations to parser, or None. """ _rich_traceback_omit = True @@ -405,8 +421,8 @@ class DOMQuery(Generic[QueryType]): """Refresh matched nodes. Args: - repaint: Repaint node(s). defaults to True. - layout: Layout node(s). Defaults to False. + repaint: Repaint node(s). + layout: Layout node(s). Returns: Query for chaining. diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 1a4b246b6..e751033ae 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -447,7 +447,7 @@ class StylesBase(ABC): Args: rule: Name of rule. - default: Default if rule does not exists. Defaults to None. + default: Default if rule does not exists. Returns: Rule value or default. @@ -511,7 +511,7 @@ class StylesBase(ABC): Args: css: Textual CSS. path: Path or string indicating source of CSS. - node: Node to associate with the Styles. Defaults to None. + node: Node to associate with the Styles. Returns: A Styles instance containing result of parsing CSS. @@ -1078,11 +1078,11 @@ class RenderStyles(StylesBase): attribute: Name of the attribute to animate. value: The value to animate to. final_value: The final value of the animation. Defaults to `value` if not set. - duration: The duration of the animate. Defaults to None. - speed: The speed of the animation. Defaults to None. - delay: A delay (in seconds) before the animation starts. Defaults to 0.0. - easing: An easing method. Defaults to "in_out_cubic". - on_complete: A callable to invoke when the animation is finished. Defaults to None. + duration: The duration of the animate. + speed: The speed of the animation. + delay: A delay (in seconds) before the animation starts. + easing: An easing method. + on_complete: A callable to invoke when the animation is finished. """ if self._animate is None: diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index e3f9dd0b1..7a48786de 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -275,7 +275,6 @@ class Stylesheet: Args: css: String with CSS source. path: The path of the source if a file, or some other identifier. - Defaults to None. is_default_css: True if the CSS is defined in the Widget, False if the CSS is defined in a user stylesheet. tie_breaker: Integer representing the priority of this source. @@ -365,12 +364,12 @@ class Stylesheet: """Apply the stylesheet to a DOM node. Args: - node: The ``DOMNode`` to apply the stylesheet to. - Applies the styles defined in this ``Stylesheet`` to the node. + node: The `DOMNode` to apply the stylesheet to. + Applies the styles defined in this `Stylesheet` to the node. If the same rule is defined multiple times for the node (e.g. multiple classes modifying the same CSS property), then only the most specific rule will be applied. - animate: Animate changed rules. Defaults to ``False``. + animate: Animate changed rules. """ # Dictionary of rule attribute names e.g. "text_background" to list of tuples. # The tuples contain the rule specificity, and the value for that rule. @@ -447,7 +446,7 @@ class Stylesheet: Args: node: A DOM node. rules: Mapping of rules. - animate: Enable animation. Defaults to False. + animate: Enable animation. """ # Alias styles and base styles @@ -519,7 +518,7 @@ class Stylesheet: Args: root: Root note to update. - animate: Enable CSS animation. Defaults to False. + animate: Enable CSS animation. """ self.update_nodes(root.walk_children(with_self=True), animate=animate) @@ -529,7 +528,7 @@ class Stylesheet: Args: nodes: Nodes to update. - animate: Enable CSS animation. Defaults to False. + animate: Enable CSS animation. """ rules_map = self.rules_map diff --git a/src/textual/css/tokenizer.py b/src/textual/css/tokenizer.py index 3128b0a76..2f35d760f 100644 --- a/src/textual/css/tokenizer.py +++ b/src/textual/css/tokenizer.py @@ -32,7 +32,7 @@ class TokenError(Exception): code: The code being parsed. start: Line number of the error. message: A message associated with the error. - end: End location of token, or None if not known. Defaults to None. + end: End location of token, or None if not known. """ self.path = path diff --git a/src/textual/demo.py b/src/textual/demo.py index 4fa0f32d6..3cb55b027 100644 --- a/src/textual/demo.py +++ b/src/textual/demo.py @@ -410,8 +410,8 @@ class DemoApp(App[None]): """Save an SVG "screenshot". This action will save an SVG file containing the current contents of the screen. Args: - filename: Filename of screenshot, or None to auto-generate. Defaults to None. - path: Path to directory. Defaults to "./". + filename: Filename of screenshot, or None to auto-generate. + path: Path to directory. """ self.bell() path = self.save_screenshot(filename, path) diff --git a/src/textual/design.py b/src/textual/design.py index 4830ac20c..5bf8a6c6c 100644 --- a/src/textual/design.py +++ b/src/textual/design.py @@ -94,12 +94,6 @@ class ColorSystem: def generate(self) -> dict[str, str]: """Generate a mapping of color name on to a CSS color. - Args: - dark: Enable dark mode. Defaults to False. - luminosity_spread: Amount of luminosity to subtract and add to generate - shades. Defaults to 0.2. - text_alpha: Alpha value for text. Defaults to 0.9. - Returns: A mapping of color name on to a CSS-style encoded color diff --git a/src/textual/dom.py b/src/textual/dom.py index 0ab188ef1..749be1559 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1,3 +1,12 @@ +""" + +A DOMNode is a base class for any object within the Textual Document Object Model, +which includes all Widgets, Screens, and Apps. + + +""" + + from __future__ import annotations import re @@ -25,7 +34,7 @@ from ._context import NoActiveAppError from ._node_list import NodeList from ._types import WatchCallbackType from ._worker_manager import WorkerManager -from .binding import Binding, Bindings, BindingType +from .binding import Binding, BindingType, _Bindings from .color import BLACK, WHITE, Color from .css._error_tools import friendly_list from .css.constants import VALID_DISPLAY, VALID_VISIBILITY @@ -134,7 +143,7 @@ class DOMNode(MessagePump): _css_type_names: ClassVar[frozenset[str]] = frozenset() # Generated list of bindings - _merged_bindings: ClassVar[Bindings | None] = None + _merged_bindings: ClassVar[_Bindings | None] = None _reactives: ClassVar[dict[str, Reactive]] @@ -168,7 +177,7 @@ class DOMNode(MessagePump): self._auto_refresh_timer: Timer | None = None self._css_types = {cls.__name__ for cls in self._css_bases(self.__class__)} self._bindings = ( - Bindings() + _Bindings() if self._merged_bindings is None else self._merged_bindings.copy() ) @@ -191,7 +200,12 @@ class DOMNode(MessagePump): @property def children(self) -> Sequence["Widget"]: - """A view on to the children.""" + """A view on to the children. + + Returns: + The node's children. + + """ return self._nodes @property @@ -212,7 +226,7 @@ class DOMNode(MessagePump): @property def workers(self) -> WorkerManager: - """A worker manager.""" + """The app's worker manager. Shortcut for `self.app.workers`.""" return self.app.workers def run_worker( @@ -346,27 +360,27 @@ class DOMNode(MessagePump): return classes @classmethod - def _merge_bindings(cls) -> Bindings: + def _merge_bindings(cls) -> _Bindings: """Merge bindings from base classes. Returns: Merged bindings. """ - bindings: list[Bindings] = [] + bindings: list[_Bindings] = [] for base in reversed(cls.__mro__): if issubclass(base, DOMNode): if not base._inherit_bindings: bindings.clear() bindings.append( - Bindings( + _Bindings( base.__dict__.get("BINDINGS", []), ) ) keys: dict[str, Binding] = {} for bindings_ in bindings: keys.update(bindings_.keys) - return Bindings(keys.values()) + return _Bindings(keys.values()) def _post_register(self, app: App) -> None: """Called when the widget is registered @@ -503,6 +517,7 @@ class DOMNode(MessagePump): return tokens classes = _ClassesDescriptor() + """CSS class names for this node.""" @property def pseudo_classes(self) -> frozenset[str]: @@ -584,9 +599,15 @@ class DOMNode(MessagePump): @property def tree(self) -> Tree: - """Get a Rich tree object which will recursively render the structure of the node tree.""" + """Get a Rich tree object which will recursively render the structure of the node tree. + + Returns: + A Rich Tree renderable. + + """ def render_info(node: DOMNode) -> Pretty: + """Render a node for the tree.""" return Pretty(node) tree = Tree(render_info(self)) @@ -605,6 +626,9 @@ class DOMNode(MessagePump): def css_tree(self) -> Tree: """Get a Rich tree object which will recursively render the structure of the node tree, which also displays CSS and size information. + + Returns: + A Rich Tree renderable. """ from rich.columns import Columns from rich.console import Group @@ -613,6 +637,7 @@ class DOMNode(MessagePump): from .widget import Widget def render_info(node: DOMNode) -> Columns: + """Render a node for the tree.""" if isinstance(node, Widget): info = Columns( [ @@ -659,7 +684,7 @@ class DOMNode(MessagePump): the child will also be bold. Returns: - Rich Style object. + A Rich Style. """ return Style.combine( node.styles.text_style for node in reversed(self.ancestors_with_self) @@ -667,7 +692,12 @@ class DOMNode(MessagePump): @property def rich_style(self) -> Style: - """Get a Rich Style object for this DOMNode.""" + """Get a Rich Style object for this DOMNode. + + Returns: + A Rich style. + + """ background = Color(0, 0, 0, 0) color = Color(255, 255, 255, 0) style = Style() @@ -720,6 +750,9 @@ class DOMNode(MessagePump): Note: This is inclusive of ``self``. + + Returns: + A list of nodes. """ nodes: list[MessagePump | None] = [] add_node = nodes.append @@ -731,12 +764,22 @@ class DOMNode(MessagePump): @property def ancestors(self) -> list[DOMNode]: - """A list of ancestor nodes Nodes by tracing ancestors all the way back to App.""" + """A list of ancestor nodes Nodes by tracing ancestors all the way back to App. + + Returns: + A list of nodes. + + """ return self.ancestors_with_self[1:] @property def displayed_children(self) -> list[Widget]: - """The children which don't have display: none set.""" + """The children which don't have display: none set. + + Returns: + A list of nodes. + + """ return [child for child in self._nodes if child.display] def watch( @@ -829,10 +872,9 @@ class DOMNode(MessagePump): Args: filter_type: Filter only this type, or None for no filter. - Defaults to None. - with_self: Also yield self in addition to descendants. Defaults to False. - method: One of "depth" or "breadth". Defaults to "depth". - reverse: Reverse the order (bottom up). Defaults to False. + with_self: Also yield self in addition to descendants. + method: One of "depth" or "breadth". + reverse: Reverse the order (bottom up). Returns: A list of nodes. @@ -867,7 +909,7 @@ class DOMNode(MessagePump): """Get a DOM query matching a selector. Args: - selector: A CSS selector or `None` for all nodes. Defaults to None. + selector: A CSS selector or `None` for all nodes. Returns: A query object. @@ -902,7 +944,6 @@ class DOMNode(MessagePump): Args: selector: A selector. expect_type: Require the object be of the supplied type, or None for any type. - Defaults to None. Raises: WrongType: If the wrong type was found. diff --git a/src/textual/events.py b/src/textual/events.py index 695912f67..f2484b23d 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -1,3 +1,17 @@ +""" + +Builtin events sent by Textual. + +Events may be marked as "Bubbles" and "Verbose". +See the [events guide](/guide/events/#bubbling) for an explanation of bubbling. +Verbose events are excluded from the textual console, unless you explicit request them with the `-v` switch as follows: + +``` +textual console -v +``` + +""" + from __future__ import annotations from typing import TYPE_CHECKING, Type, TypeVar @@ -37,7 +51,12 @@ class Callback(Event, bubble=False, verbose=True): class InvokeCallbacks(Event, bubble=False, verbose=True): - """Sent after the Screen is updated""" + """An internal event, sent to the screen to run callbacks. + + - [ ] Bubbles + - [X] Verbose + + """ class ShutdownRequest(Event): @@ -55,6 +74,8 @@ class Load(Event, bubble=False): Use this event to run any set up that doesn't require any visuals such as loading configuration and binding keys. + - [ ] Bubbles + - [ ] Verbose """ @@ -65,6 +86,9 @@ class Idle(Event, bubble=False): This is a pseudo-event in that it is created by the Textual system and doesn't go through the usual message queue. + - [ ] Bubbles + - [ ] Verbose + """ @@ -81,10 +105,14 @@ class Action(Event): class Resize(Event, bubble=False): """Sent when the app or widget has been resized. + + - [ ] Bubbles + - [ ] Verbose + Args: size: The new size of the Widget. virtual_size: The virtual size (scrollable size) of the Widget. - container_size: The size of the Widget's container widget. Defaults to None. + container_size: The size of the Widget's container widget. """ @@ -111,24 +139,47 @@ class Resize(Event, bubble=False): class Compose(Event, bubble=False, verbose=True): - """Sent to a widget to request it to compose and mount children.""" + """Sent to a widget to request it to compose and mount children. + + - [ ] Bubbles + - [X] Verbose + + """ class Mount(Event, bubble=False, verbose=False): - """Sent when a widget is *mounted* and may receive messages.""" + """Sent when a widget is *mounted* and may receive messages. + + - [ ] Bubbles + - [ ] Verbose + + """ class Unmount(Mount, bubble=False, verbose=False): - """Sent when a widget is unmounted and may not longer receive messages.""" + """Sent when a widget is unmounted and may not longer receive messages. + + - [ ] Bubbles + - [ ] Verbose + + """ class Show(Event, bubble=False): - """Sent when a widget has become visible.""" + """Sent when a widget has become visible. + + - [ ] Bubbles + - [ ] Verbose + + """ class Hide(Event, bubble=False): """Sent when a widget has been hidden. + - [ ] Bubbles + - [ ] Verbose + A widget may be hidden by setting its `visible` flag to `False`, if it is no longer in a layout, or if it has been offset beyond the edges of the terminal. @@ -136,15 +187,22 @@ class Hide(Event, bubble=False): class Ready(Event, bubble=False): - """Sent to the app when the DOM is ready.""" + """Sent to the app when the DOM is ready. + + - [ ] Bubbles + - [ ] Verbose + + """ @rich.repr.auto class MouseCapture(Event, bubble=False): """Sent when the mouse has been captured. - When a mouse has been captured, all further mouse events will be sent to the capturing widget. + - [ ] Bubbles + - [ ] Verbose + When a mouse has been captured, all further mouse events will be sent to the capturing widget. Args: mouse_position: The position of the mouse when captured. @@ -163,6 +221,9 @@ class MouseCapture(Event, bubble=False): class MouseRelease(Event, bubble=False): """Mouse has been released. + - [ ] Bubbles + - [ ] Verbose + Args: mouse_position: The position of the mouse when released. """ @@ -183,6 +244,9 @@ class InputEvent(Event): class Key(InputEvent): """Sent when the user hits a key on the keyboard. + - [X] Bubbles + - [ ] Verbose + Args: key: The key that was pressed. character: A printable character or ``None`` if it is not printable. @@ -239,6 +303,9 @@ def _key_to_identifier(key: str) -> str: class MouseEvent(InputEvent, bubble=True): """Sent in response to a mouse event. + - [X] Bubbles + - [ ] Verbose + Args: x: The relative x coordinate. y: The relative y coordinate. @@ -395,35 +462,73 @@ class MouseEvent(InputEvent, bubble=True): @rich.repr.auto class MouseMove(MouseEvent, bubble=False, verbose=True): - """Sent when the mouse cursor moves.""" + """Sent when the mouse cursor moves. + + - [ ] Bubbles + - [X] Verbose + + """ @rich.repr.auto class MouseDown(MouseEvent, bubble=True, verbose=True): - pass + """Sent when a mouse button is pressed. + + - [X] Bubbles + - [X] Verbose + + """ @rich.repr.auto class MouseUp(MouseEvent, bubble=True, verbose=True): - pass + """Sent when a mouse button is released. + + - [X] Bubbles + - [X] Verbose + + """ @rich.repr.auto class MouseScrollDown(MouseEvent, bubble=True): - pass + """Sent when the mouse wheel is scrolled *down*. + + - [X] Bubbles + - [ ] Verbose + + """ @rich.repr.auto class MouseScrollUp(MouseEvent, bubble=True): - pass + """Sent when the mouse wheel is scrolled *up*. + + - [X] Bubbles + - [ ] Verbose + + """ class Click(MouseEvent, bubble=True): - pass + """Sent when a widget is clicked. + + - [X] Bubbles + - [ ] Verbose + + """ @rich.repr.auto class Timer(Event, bubble=False, verbose=True): + """Sent in response to a timer. + + - [ ] Bubbles + - [X] Verbose + + + """ + __slots__ = ["time", "count", "callback"] def __init__( @@ -445,27 +550,57 @@ class Timer(Event, bubble=False, verbose=True): class Enter(Event, bubble=False, verbose=True): - pass + """Sent when the mouse is moved over a widget. + + - [ ] Bubbles + - [X] Verbose + + """ class Leave(Event, bubble=False, verbose=True): - pass + """Sent when the mouse is moved away from a widget. + + - [ ] Bubbles + - [X] Verbose + + """ class Focus(Event, bubble=False): - pass + """Sent when a widget is focussed. + + - [X] Bubbles + - [ ] Verbose + + """ class Blur(Event, bubble=False): - pass + """Sent when a widget is blurred (un-focussed). + + - [X] Bubbles + - [ ] Verbose + + """ class DescendantFocus(Event, bubble=True, verbose=True): - pass + """Sent when a child widget is focussed. + + - [X] Bubbles + - [X] Verbose + + """ class DescendantBlur(Event, bubble=True, verbose=True): - pass + """Sent when a child widget is blurred. + + - [X] Bubbles + - [X] Verbose + + """ @rich.repr.auto @@ -475,6 +610,10 @@ class Paste(Event, bubble=True): bracketed paste mode. Textual will enable bracketed pastes when an app starts, and disable it when the app shuts down. + - [X] Bubbles + - [ ] Verbose + + Args: text: The text that has been pasted. """ @@ -488,8 +627,18 @@ class Paste(Event, bubble=True): class ScreenResume(Event, bubble=False): - pass + """Sent to screen that has been made active. + + - [ ] Bubbles + - [ ] Verbose + + """ class ScreenSuspend(Event, bubble=False): - pass + """Sent to screen when it is no longer active. + + - [ ] Bubbles + - [ ] Verbose + + """ diff --git a/src/textual/geometry.py b/src/textual/geometry.py index e6efbbe5d..7a52eb7b7 100644 --- a/src/textual/geometry.py +++ b/src/textual/geometry.py @@ -19,6 +19,8 @@ from typing import ( cast, ) +from typing_extensions import Final + if TYPE_CHECKING: from typing_extensions import TypeAlias @@ -31,7 +33,7 @@ T = TypeVar("T", int, float) def clamp(value: T, minimum: T, maximum: T) -> T: - """Adjust a value to it is not less than a minimum and not greater + """Adjust a value so it is not less than a minimum and not greater than a maximum value. Args: @@ -53,8 +55,9 @@ def clamp(value: T, minimum: T, maximum: T) -> T: class Offset(NamedTuple): - """A cell offset defined by x and y coordinates. Offsets are typically relative to the - top left of the terminal or other container. + """A cell offset defined by x and y coordinates. + + Offsets are typically relative to the top left of the terminal or other container. Textual prefers the names `x` and `y`, but you could consider `x` to be the _column_ and `y` to be the _row_. @@ -67,21 +70,12 @@ class Offset(NamedTuple): @property def is_origin(self) -> bool: - """Check if the point is at the origin (0, 0). - - Returns: - True if the offset is the origin. - - """ + """Is the offset at (0, 0)?""" return self == (0, 0) @property def clamped(self) -> Offset: - """Ensure x and y are above zero. - - Returns: - New offset. - """ + """This offset with `x` and `y` restricted to values above zero.""" x, y = self return Offset(0 if x < 0 else x, 0 if y < 0 else y) @@ -113,7 +107,7 @@ class Offset(NamedTuple): return Offset(-x, -y) def blend(self, destination: Offset, factor: float) -> Offset: - """Blend (interpolate) to a new point. + """Calculate a new offset on a line between this offset and a destination offset. Args: destination: Point where factor would be 1.0. @@ -159,31 +153,18 @@ class Size(NamedTuple): @property def area(self) -> int: - """Get the area of the size. - - Returns: - Area in cells. - """ + """The area occupied by a region of this size.""" return self.width * self.height @property def region(self) -> Region: - """Get a region of the same size. - - Returns: - A region with the same size at (0, 0). - - """ + """A region of the same size, at the origin.""" width, height = self return Region(0, 0, width, height) @property def line_range(self) -> range: - """Get a range covering lines. - - Returns: - A builtin range object. - """ + """A range object that covers values between 0 and `height`.""" return range(self.height) def __add__(self, other: object) -> Size: @@ -327,7 +308,7 @@ class Region(NamedTuple): Args: window_region: The window region. region: The region to move inside the window. - top: Get offset to top of window. Defaults to False + top: Get offset to top of window. Returns: An offset required to add to region to move it inside window_region. @@ -376,64 +357,43 @@ class Region(NamedTuple): @property def column_span(self) -> tuple[int, int]: - """Get the start and end columns (x coord). + """A pair of integers for the start and end columns (x coordinates) in this region. - The end value is exclusive. - - Returns: - Pair of x coordinates (column numbers). + The end value is *exclusive*. """ return (self.x, self.x + self.width) @property def line_span(self) -> tuple[int, int]: - """Get the start and end line number (y coord). + """A pair of integers for the start and end lines (y coordinates) in this region. - The end value is exclusive. - - Returns: - Pair of y coordinates (line numbers). + The end value is *exclusive*. """ return (self.y, self.y + self.height) @property def right(self) -> int: - """Maximum X value (non inclusive). - - Returns: - x coordinate. - - """ + """Maximum X value (non inclusive).""" return self.x + self.width @property def bottom(self) -> int: - """Maximum Y value (non inclusive). - - Returns: - y coordinate. - - """ + """Maximum Y value (non inclusive).""" return self.y + self.height @property def area(self) -> int: - """Get the area within the region. - - Returns: - Area covered by this region. - - """ + """The are under the region.""" return self.width * self.height @property def offset(self) -> Offset: - """Get the start point of the region. + """The top left corner of the region. Returns: - Top left offset. + An offset. """ return Offset(*self[:2]) @@ -443,8 +403,7 @@ class Region(NamedTuple): """Bottom left offset of the region. Returns: - Bottom left offset. - + An offset. """ x, y, _width, height = self return Offset(x, y + height) @@ -454,7 +413,7 @@ class Region(NamedTuple): """Top right offset of the region. Returns: - Top right. + An offset. """ x, y, width, _height = self @@ -462,10 +421,10 @@ class Region(NamedTuple): @property def bottom_right(self) -> Offset: - """Bottom right of the region. + """Bottom right offset of the region. Returns: - Bottom right. + An offset. """ x, y, width, height = self @@ -473,21 +432,12 @@ class Region(NamedTuple): @property def size(self) -> Size: - """Get the size of the region. - - Returns: - Size of the region. - - """ + """Get the size of the region.""" return Size(*self[2:]) @property def corners(self) -> tuple[int, int, int, int]: - """Get the top left and bottom right coordinates as a tuple of integers. - - Returns: - A tuple of `(, , , )`. - """ + """The top left and bottom right coordinates as a tuple of four integers.""" x, y, width, height = self return x, y, x + width, y + height @@ -506,7 +456,7 @@ class Region(NamedTuple): """An region of the same size at (0, 0). Returns: - Reset region. + A region at the origin. """ _, _, width, height = self @@ -897,63 +847,35 @@ class Spacing(NamedTuple): @property def width(self) -> int: - """Total space in width. - - Returns: - Width. - - """ + """Total space in the x axis.""" return self.left + self.right @property def height(self) -> int: - """Total space in height. - - Returns: - Height. - - """ + """Total space in the y axis.""" return self.top + self.bottom @property def top_left(self) -> tuple[int, int]: - """Top left space. - - Returns: - `(, )` - - """ + """A pair of integers for the left, and top space.""" return (self.left, self.top) @property def bottom_right(self) -> tuple[int, int]: - """Bottom right space. - - Returns: - `(, )` - - """ + """A pair of integers for the right, and bottom space.""" return (self.right, self.bottom) @property def totals(self) -> tuple[int, int]: - """Get total horizontal and vertical space. - - Returns: - `(, )` - - - """ + """A pair of integers for the total horizontal and vertical space.""" top, right, bottom, left = self return (left + right, top + bottom) @property def css(self) -> str: - """Gets a string containing the spacing in CSS format. - - Returns: - Spacing in CSS format. + """A string containing the spacing in CSS format. + For example: "1" or "2 4" or "4 2 8 2". """ top, right, bottom, left = self if top == right == bottom == left: @@ -1067,4 +989,5 @@ class Spacing(NamedTuple): ) -NULL_OFFSET = Offset(0, 0) +NULL_OFFSET: Final = Offset(0, 0) +"""An Offset constant for (0, 0).""" diff --git a/src/textual/logging.py b/src/textual/logging.py index 1ceb1bd2a..6f73e62cf 100644 --- a/src/textual/logging.py +++ b/src/textual/logging.py @@ -1,3 +1,13 @@ +""" +A Textual Logging handler. + +If there is an active Textual app, then log messages will go via the app (and logged via textual console). + +If there is *no* active app, then log messages will go to stderr or stdout, depending on configuration. + +""" + + import sys from logging import Handler, LogRecord diff --git a/src/textual/message.py b/src/textual/message.py index e4a16e827..9ff65491b 100644 --- a/src/textual/message.py +++ b/src/textual/message.py @@ -1,3 +1,10 @@ +""" + +The base class for all messages (including events). + + +""" + from __future__ import annotations from typing import TYPE_CHECKING, ClassVar @@ -99,7 +106,7 @@ class Message: Args: prevent: True if the default action should be suppressed, - or False if the default actions should be performed. Defaults to True. + or False if the default actions should be performed. """ self._no_default_action = prevent return self @@ -108,7 +115,7 @@ class Message: """Stop propagation of the message to parent. Args: - stop: The stop flag. Defaults to True. + stop: The stop flag. """ self._stop_propagation = stop return self diff --git a/src/textual/message_pump.py b/src/textual/message_pump.py index cbdeabef5..7dea983b6 100644 --- a/src/textual/message_pump.py +++ b/src/textual/message_pump.py @@ -1,8 +1,6 @@ """ -A message pump is a class that processes messages. - -It is a base class for the App, Screen, and Widgets. +A message pump is a base class for any object which processes messages, which includes Widget, Screen, and App. """ from __future__ import annotations @@ -46,7 +44,7 @@ class MessagePumpClosed(Exception): pass -class MessagePumpMeta(type): +class _MessagePumpMeta(type): """Metaclass for message pump. This exists to populate a Message inner class of a Widget with the parent classes' name. @@ -69,7 +67,7 @@ class MessagePumpMeta(type): return class_obj -class MessagePump(metaclass=MessagePumpMeta): +class MessagePump(metaclass=_MessagePumpMeta): """Base class which supplies a message pump.""" def __init__(self, parent: MessagePump | None = None) -> None: @@ -143,6 +141,7 @@ class MessagePump(metaclass=MessagePumpMeta): @property def has_parent(self) -> bool: + """Does this object have a parent?""" return self._parent is not None @property @@ -178,7 +177,7 @@ class MessagePump(metaclass=MessagePumpMeta): @property def is_running(self) -> bool: - """Is the message pump running (potentially processing messages).""" + """Is the message pump running (potentially processing messages)?""" return self._running @property @@ -192,7 +191,7 @@ class MessagePump(metaclass=MessagePumpMeta): @property def is_attached(self) -> bool: - """Is the node is attached to the app via the DOM.""" + """Is the node is attached to the app via the DOM?""" from .app import App node = self @@ -289,9 +288,9 @@ class MessagePump(metaclass=MessagePumpMeta): Args: delay: Time to wait before invoking callback. - callback: Callback to call after time has expired. Defaults to None. - name: Name of the timer (for debug). Defaults to None. - pause: Start timer paused. Defaults to False. + callback: Callback to call after time has expired. + name: Name of the timer (for debug). + pause: Start timer paused. Returns: A timer object. @@ -321,10 +320,10 @@ class MessagePump(metaclass=MessagePumpMeta): Args: interval: Time between calls. - callback: Function to call. Defaults to None. - name: Name of the timer object. Defaults to None. - repeat: Number of times to repeat the call or 0 for continuous. Defaults to 0. - pause: Start the timer paused. Defaults to False. + callback: Function to call. + name: Name of the timer object. + repeat: Number of times to repeat the call or 0 for continuous. + pause: Start the timer paused. Returns: A timer object. diff --git a/src/textual/pilot.py b/src/textual/pilot.py index 6e2ea0239..e1786421d 100644 --- a/src/textual/pilot.py +++ b/src/textual/pilot.py @@ -1,3 +1,9 @@ +""" + +The pilot object is used by [App.run_test][textual.app.App.run_test] to programmatically operate an app. + +""" + from __future__ import annotations import asyncio diff --git a/src/textual/reactive.py b/src/textual/reactive.py index 8209fe024..c02c28638 100644 --- a/src/textual/reactive.py +++ b/src/textual/reactive.py @@ -1,3 +1,9 @@ +""" + +The `Reactive` class implements [reactivity](/guide/reactivity/). + +""" + from __future__ import annotations from functools import partial @@ -33,11 +39,11 @@ class Reactive(Generic[ReactiveType]): Args: default: A default value or callable that returns a default. - layout: Perform a layout on change. Defaults to False. - repaint: Perform a repaint on change. Defaults to True. - init: Call watchers on initialize (post mount). Defaults to False. - always_update: Call watchers even when the new value equals the old value. Defaults to False. - compute: Run compute methods when attribute is changed. Defaults to True. + layout: Perform a layout on change. + repaint: Perform a repaint on change. + init: Call watchers on initialize (post mount). + always_update: Call watchers even when the new value equals the old value. + compute: Run compute methods when attribute is changed. """ _reactives: ClassVar[dict[str, object]] = {} @@ -280,10 +286,10 @@ class reactive(Reactive[ReactiveType]): Args: default: A default value or callable that returns a default. - layout: Perform a layout on change. Defaults to False. - repaint: Perform a repaint on change. Defaults to True. - init: Call watchers on initialize (post mount). Defaults to True. - always_update: Call watchers even when the new value equals the old value. Defaults to False. + layout: Perform a layout on change. + repaint: Perform a repaint on change. + init: Call watchers on initialize (post mount). + always_update: Call watchers even when the new value equals the old value. """ def __init__( @@ -309,7 +315,7 @@ class var(Reactive[ReactiveType]): Args: default: A default value or callable that returns a default. - init: Call watchers on initialize (post mount). Defaults to True. + init: Call watchers on initialize (post mount). """ def __init__( @@ -339,7 +345,7 @@ def _watch( obj: The parent object. attribute_name: The attribute to watch. callback: A callable to call when the attribute changes. - init: True to call watcher initialization. Defaults to True. + init: True to call watcher initialization. """ if not hasattr(obj, "__watchers"): setattr(obj, "__watchers", {}) diff --git a/src/textual/renderables/underline_bar.py b/src/textual/renderables/underline_bar.py index 4a7f2ea35..18788d0a5 100644 --- a/src/textual/renderables/underline_bar.py +++ b/src/textual/renderables/underline_bar.py @@ -9,7 +9,7 @@ class UnderlineBar: """Thin horizontal bar with a portion highlighted. Args: - highlight_range: The range to highlight. Defaults to ``(0, 0)`` (no highlight) + highlight_range: The range to highlight. highlight_style: The style of the highlighted range of the bar. background_style: The style of the non-highlighted range(s) of the bar. width: The width of the bar, or ``None`` to fill available width. diff --git a/src/textual/screen.py b/src/textual/screen.py index 170c00e2d..7d7a22bc2 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -1,3 +1,9 @@ +""" + +The `Screen` class is a special widget which represents the content in the terminal. See [Screens](/guide/screens/) for details. + +""" + from __future__ import annotations from typing import TYPE_CHECKING, Iterable, Iterator diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index dcf6b17cf..e791471e5 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -1,3 +1,8 @@ +""" +`ScrollView` is a base class for [line api](/guide/widgets#line-api) widgets. + +""" + from __future__ import annotations from rich.console import RenderableType diff --git a/src/textual/strip.py b/src/textual/strip.py index d6a7d331d..2470294df 100644 --- a/src/textual/strip.py +++ b/src/textual/strip.py @@ -1,3 +1,9 @@ +""" +A Strip contains the result of rendering a widget. +See [line API](/guide/widgets#line-api) for how to use Strips. +""" + + from __future__ import annotations from itertools import chain @@ -51,7 +57,7 @@ class Strip: Args: segments: An iterable of segments. - cell_length: The cell length if known, or None to calculate on demand. Defaults to None. + cell_length: The cell length if known, or None to calculate on demand. """ __slots__ = [ @@ -117,7 +123,7 @@ class Strip: Args: lines: List of lines, where a line is a list of segments. - cell_length: Cell length of lines (must be same) or None if not known. Defaults to None. + cell_length: Cell length of lines (must be same) or None if not known. Returns: List of strips. @@ -205,7 +211,7 @@ class Strip: Args: cell_length: New desired cell length. - style: Style when extending, or `None`. Defaults to `None`. + style: Style when extending, or `None`. Returns: A new strip with the supplied cell length. diff --git a/src/textual/timer.py b/src/textual/timer.py index a6c23c7af..4def1271e 100644 --- a/src/textual/timer.py +++ b/src/textual/timer.py @@ -34,11 +34,11 @@ class Timer: Args: event_target: The object which will receive the timer events. interval: The time between timer events, in seconds. - name: A name to assign the event (for debugging). Defaults to None. - callback: A optional callback to invoke when the event is handled. Defaults to None. - repeat: The number of times to repeat the timer, or None to repeat forever. Defaults to None. - skip: Enable skipping of scheduled events that couldn't be sent in time. Defaults to True. - pause: Start the timer paused. Defaults to False. + name: A name to assign the event (for debugging). + callback: A optional callback to invoke when the event is handled. + repeat: The number of times to repeat the timer, or None to repeat forever. + skip: Enable skipping of scheduled events that couldn't be sent in time. + pause: Start the timer paused. """ _timer_count: int = 1 diff --git a/src/textual/walk.py b/src/textual/walk.py index 0dc672471..ed4b4e501 100644 --- a/src/textual/walk.py +++ b/src/textual/walk.py @@ -1,3 +1,12 @@ +""" +Functions for *walking* the DOM. + +!!! note + + For most purposes you would be better off using [query][textual.dom.DOMNode.query], which uses these functions internally. + +""" + from __future__ import annotations from collections import deque @@ -44,8 +53,7 @@ def walk_depth_first( Args: root: The root note (starting point). filter_type: Optional DOMNode subclass to filter by, or ``None`` for no filter. - Defaults to None. - with_root: Include the root in the walk. Defaults to True. + with_root: Include the root in the walk. Returns: An iterable of DOMNodes, or the type specified in ``filter_type``. @@ -106,8 +114,7 @@ def walk_breadth_first( Args: root: The root note (starting point). filter_type: Optional DOMNode subclass to filter by, or ``None`` for no filter. - Defaults to None. - with_root: Include the root in the walk. Defaults to True. + with_root: Include the root in the walk. Returns: An iterable of DOMNodes, or the type specified in ``filter_type``. diff --git a/src/textual/widget.py b/src/textual/widget.py index 9e802c5c3..0ab902423 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1,3 +1,8 @@ +""" +The base class for widgets. + +""" + from __future__ import annotations from asyncio import Lock, wait @@ -84,10 +89,12 @@ _JUSTIFY_MAP: dict[str, JustifyMethod] = { class AwaitMount: - """An awaitable returned by mount() and mount_all(). + """An *optional* awaitable returned by [mount][textual.widget.Widget.mount] and [mount_all][textual.widget.Widget.mount_all]. Example: + ```python await self.mount(Static("foo")) + ``` """ @@ -164,7 +171,7 @@ class _Styled: return Measurement.get(console, options, self.renderable) -class RenderCache(NamedTuple): +class _RenderCache(NamedTuple): """Stores results of a previous render.""" size: Size @@ -286,6 +293,15 @@ class Widget(DOMNode): classes: str | None = None, disabled: bool = False, ) -> None: + """Initialize a Widget. + + Args: + *children: Child widgets. + name: The name of the button. + id: The ID of the button in the DOM. + classes: The CSS classes of the button. + disabled: Whether the button is disabled or not. + """ self._size = Size(0, 0) self._container_size = Size(0, 0) self._layout_required = False @@ -302,7 +318,7 @@ class Widget(DOMNode): self._border_title: Text | None = None self._border_subtitle: Text | None = None - self._render_cache = RenderCache(Size(0, 0), []) + self._render_cache = _RenderCache(Size(0, 0), []) # Regions which need to be updated (in Widget) self._dirty_regions: set[Region] = set() # Regions which need to be transferred from cache to screen @@ -469,7 +485,6 @@ class Widget(DOMNode): Args: id: The ID of the child. expect_type: Require the object be of the supplied type, or None for any type. - Defaults to None. Returns: The first child of this node with the ID. @@ -1498,11 +1513,11 @@ class Widget(DOMNode): attribute: Name of the attribute to animate. value: The value to animate to. final_value: The final value of the animation. Defaults to `value` if not set. - duration: The duration of the animate. Defaults to None. - speed: The speed of the animation. Defaults to None. - delay: A delay (in seconds) before the animation starts. Defaults to 0.0. - easing: An easing method. Defaults to "in_out_cubic". - on_complete: A callable to invoke when the animation is finished. Defaults to None. + duration: The duration of the animate. + speed: The speed of the animation. + delay: A delay (in seconds) before the animation starts. + easing: An easing method. + on_complete: A callable to invoke when the animation is finished. """ if self._animate is None: @@ -2663,7 +2678,7 @@ class Widget(DOMNode): ) ) strips = [Strip(line, width) for line in lines] - self._render_cache = RenderCache(self.size, strips) + self._render_cache = _RenderCache(self.size, strips) self._dirty_regions.clear() def render_line(self, y: int) -> Strip: @@ -2886,7 +2901,7 @@ class Widget(DOMNode): When captured, mouse events will go to this widget even when the pointer is not directly over the widget. Args: - capture: True to capture or False to release. Defaults to True. + capture: True to capture or False to release. """ self.app.capture_mouse(self if capture else None) diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 3d32b5f4b..69d632722 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -553,7 +553,7 @@ class Markdown(Widget): """A Markdown widget. Args: - markdown: String containing Markdown or None to leave blank for now. Defaults to None. + markdown: String containing Markdown or None to leave blank for now. name: The name of the widget. id: The ID of the widget in the DOM. classes: The CSS classes of the widget. @@ -850,8 +850,8 @@ class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True): """Create a Markdown Viewer object. Args: - markdown: String containing Markdown, or None to leave blank. Defaults to None. - show_table_of_contents: Show a table of contents in a sidebar. Defaults to True. + markdown: String containing Markdown, or None to leave blank. + show_table_of_contents: Show a table of contents in a sidebar. name: The name of the widget. id: The ID of the widget in the DOM. classes: The CSS classes of the widget. diff --git a/src/textual/widgets/_placeholder.py b/src/textual/widgets/_placeholder.py index 17b0614af..da5bce717 100644 --- a/src/textual/widgets/_placeholder.py +++ b/src/textual/widgets/_placeholder.py @@ -100,7 +100,7 @@ class Placeholder(Widget): label: The label to identify the placeholder. If no label is present, uses the placeholder ID instead. variant: The variant of the placeholder. - name: The name of the placeholder. Defaults to None. + name: The name of the placeholder. id: The ID of the placeholder in the DOM. classes: A space separated string with the CSS classes of the placeholder, if any. diff --git a/src/textual/widgets/_static.py b/src/textual/widgets/_static.py index 5007f1b1d..a9f485345 100644 --- a/src/textual/widgets/_static.py +++ b/src/textual/widgets/_static.py @@ -29,13 +29,12 @@ class Static(Widget, inherit_bindings=False): Args: renderable: A Rich renderable, or string containing console markup. - Defaults to "". - expand: Expand content if required to fill container. Defaults to False. - shrink: Shrink content if required to fill container. Defaults to False. - markup: True if markup should be parsed and rendered. Defaults to True. - name: Name of widget. Defaults to None. - id: ID of Widget. Defaults to None. - classes: Space separated list of class names. Defaults to None. + expand: Expand content if required to fill container. + shrink: Shrink content if required to fill container. + markup: True if markup should be parsed and rendered. + name: Name of widget. + id: ID of Widget. + classes: Space separated list of class names. disabled: Whether the static is disabled or not. """ diff --git a/src/textual/widgets/_switch.py b/src/textual/widgets/_switch.py index cf636b92a..abccfd985 100644 --- a/src/textual/widgets/_switch.py +++ b/src/textual/widgets/_switch.py @@ -113,8 +113,8 @@ class Switch(Widget, can_focus=True): """Initialise the switch. Args: - value: The initial value of the switch. Defaults to False. - animate: True if the switch should animate when toggled. Defaults to True. + value: The initial value of the switch. + animate: True if the switch should animate when toggled. name: The name of the switch. id: The ID of the switch in the DOM. classes: The CSS classes of the switch. diff --git a/src/textual/widgets/_toggle_button.py b/src/textual/widgets/_toggle_button.py index a25b283e7..6b4f76f8e 100644 --- a/src/textual/widgets/_toggle_button.py +++ b/src/textual/widgets/_toggle_button.py @@ -127,7 +127,7 @@ class ToggleButton(Static, can_focus=True): Args: label: The label for the toggle. - value: The initial value of the toggle. Defaults to `False`. + value: The initial value of the toggle. button_first: Should the button come before the label, or after? name: The name of the toggle. id: The ID of the toggle in the DOM. diff --git a/src/textual/widgets/_tree.py b/src/textual/widgets/_tree.py index 0b5034364..2e63742b4 100644 --- a/src/textual/widgets/_tree.py +++ b/src/textual/widgets/_tree.py @@ -309,8 +309,8 @@ class TreeNode(Generic[TreeDataType]): Args: label: The new node's label. data: Data associated with the new node. - expand: Node should be expanded. Defaults to True. - allow_expand: Allow use to expand the node via keyboard or mouse. Defaults to True. + expand: Node should be expanded. + allow_expand: Allow use to expand the node via keyboard or mouse. Returns: A new Tree node diff --git a/src/textual/worker.py b/src/textual/worker.py index f64ba8ec9..eb542f8e8 100644 --- a/src/textual/worker.py +++ b/src/textual/worker.py @@ -1,3 +1,8 @@ +""" +A class to manage concurrent [work](/guide/workers). + +""" + from __future__ import annotations import asyncio diff --git a/tests/test_binding.py b/tests/test_binding.py index 65a8c0285..e73ecfb3d 100644 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -3,7 +3,7 @@ from string import ascii_lowercase import pytest from textual.app import App -from textual.binding import Binding, BindingError, Bindings, InvalidBinding, NoBinding +from textual.binding import Binding, BindingError, InvalidBinding, NoBinding, _Bindings BINDING1 = Binding("a,b", action="action1", description="description1") BINDING2 = Binding("c", action="action2", description="description2") @@ -12,12 +12,12 @@ BINDING3 = Binding(" d , e ", action="action3", description="description3") @pytest.fixture def bindings(): - yield Bindings([BINDING1, BINDING2]) + yield _Bindings([BINDING1, BINDING2]) @pytest.fixture def more_bindings(): - yield Bindings([BINDING1, BINDING2, BINDING3]) + yield _Bindings([BINDING1, BINDING2, BINDING3]) def test_bindings_get_key(bindings): @@ -34,17 +34,17 @@ def test_bindings_get_key_spaced_list(more_bindings): def test_bindings_merge_simple(bindings): - left = Bindings([BINDING1]) - right = Bindings([BINDING2]) - assert Bindings.merge([left, right]).keys == bindings.keys + left = _Bindings([BINDING1]) + right = _Bindings([BINDING2]) + assert _Bindings.merge([left, right]).keys == bindings.keys def test_bindings_merge_overlap(): - left = Bindings([BINDING1]) + left = _Bindings([BINDING1]) another_binding = Binding( "a", action="another_action", description="another_description" ) - assert Bindings.merge([left, Bindings([another_binding])]).keys == { + assert _Bindings.merge([left, _Bindings([another_binding])]).keys == { "a": another_binding, "b": Binding("b", action="action1", description="description1"), } @@ -52,20 +52,20 @@ def test_bindings_merge_overlap(): def test_bad_binding_tuple(): with pytest.raises(BindingError): - _ = Bindings((("a", "action"),)) + _ = _Bindings((("a", "action"),)) with pytest.raises(BindingError): - _ = Bindings((("a", "action", "description", "too much"),)) + _ = _Bindings((("a", "action", "description", "too much"),)) def test_binding_from_tuples(): assert ( - Bindings(((BINDING2.key, BINDING2.action, BINDING2.description),)).get_key("c") + _Bindings(((BINDING2.key, BINDING2.action, BINDING2.description),)).get_key("c") == BINDING2 ) def test_shown(): - bindings = Bindings( + bindings = _Bindings( [ Binding( key,