Merge branch 'main' of github.com:Textualize/textual into import-sorting-hook

This commit is contained in:
Darren Burns
2023-02-09 14:28:23 +00:00
17 changed files with 288 additions and 286 deletions

View File

@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Breaking change: `TreeNode` can no longer be imported from `textual.widgets`; it is now available via `from textual.widgets.tree import TreeNode`. https://github.com/Textualize/textual/pull/1637
- `Tree` now shows a (subdued) cursor for a highlighted node when focus has moved elsewhere https://github.com/Textualize/textual/issues/1471
- Breaking change: renamed `Checkbox` to `Switch`.
### Fixed

View File

@@ -1 +0,0 @@
::: textual.widgets.Checkbox

1
docs/api/switch.md Normal file
View File

@@ -0,0 +1 @@
::: textual.widgets.Switch

View File

@@ -7,7 +7,7 @@ Screen {
width: auto;
}
Checkbox {
Switch {
height: auto;
width: auto;
}
@@ -22,7 +22,7 @@ Checkbox {
background: darkslategrey;
}
#custom-design > .checkbox--switch {
#custom-design > .switch--switch {
color: dodgerblue;
background: darkslateblue;
}

View File

@@ -1,35 +1,35 @@
from textual.app import App, ComposeResult
from textual.containers import Horizontal
from textual.widgets import Checkbox, Static
from textual.widgets import Switch, Static
class CheckboxApp(App):
class SwitchApp(App):
def compose(self) -> ComposeResult:
yield Static("[b]Example checkboxes\n", classes="label")
yield Static("[b]Example switches\n", classes="label")
yield Horizontal(
Static("off: ", classes="label"),
Checkbox(animate=False),
Switch(animate=False),
classes="container",
)
yield Horizontal(
Static("on: ", classes="label"),
Checkbox(value=True),
Switch(value=True),
classes="container",
)
focused_checkbox = Checkbox()
focused_checkbox.focus()
focused_switch = Switch()
focused_switch.focus()
yield Horizontal(
Static("focused: ", classes="label"), focused_checkbox, classes="container"
Static("focused: ", classes="label"), focused_switch, classes="container"
)
yield Horizontal(
Static("custom: ", classes="label"),
Checkbox(id="custom-design"),
Switch(id="custom-design"),
classes="container",
)
app = CheckboxApp(css_path="checkbox.css")
app = SwitchApp(css_path="switch.css")
if __name__ == "__main__":
app.run()

View File

@@ -40,7 +40,7 @@ Widgets are key to making user-friendly interfaces. The builtin widgets should c
- [x] Buttons
* [x] Error / warning variants
- [ ] Color picker
- [x] Checkbox
- [ ] Checkbox
- [ ] Content switcher
- [x] DataTable
* [x] Cell select
@@ -70,6 +70,7 @@ Widgets are key to making user-friendly interfaces. The builtin widgets should c
* [ ] Style variants (solid, thin etc)
- [ ] Radio boxes
- [ ] Spark-lines
- [X] Switch
- [ ] Tabs
- [ ] TextArea (multi-line input)
* [ ] Basic controls

View File

@@ -1,63 +0,0 @@
# Checkbox
A simple checkbox widget which stores a boolean value.
- [x] Focusable
- [ ] Container
## Example
The example below shows checkboxes in various states.
=== "Output"
```{.textual path="docs/examples/widgets/checkbox.py"}
```
=== "checkbox.py"
```python
--8<-- "docs/examples/widgets/checkbox.py"
```
=== "checkbox.css"
```sass
--8<-- "docs/examples/widgets/checkbox.css"
```
## Reactive Attributes
| Name | Type | Default | Description |
| ------- | ------ | ------- | ---------------------------------- |
| `value` | `bool` | `False` | The default value of the checkbox. |
## Bindings
The checkbox widget defines directly the following bindings:
::: textual.widgets.Checkbox.BINDINGS
options:
show_root_heading: false
show_root_toc_entry: false
## Component Classes
The checkbox widget provides the following component classes:
::: textual.widgets.Checkbox.COMPONENT_CLASSES
options:
show_root_heading: false
show_root_toc_entry: false
## Messages
### ::: textual.widgets.Checkbox.Changed
## Additional Notes
- To remove the spacing around a checkbox, set `border: none;` and `padding: 0;`.
## See Also
- [Checkbox](../api/checkbox.md) code reference

63
docs/widgets/switch.md Normal file
View File

@@ -0,0 +1,63 @@
# Switch
A simple switch widget which stores a boolean value.
- [x] Focusable
- [ ] Container
## Example
The example below shows switches in various states.
=== "Output"
```{.textual path="docs/examples/widgets/switch.py"}
```
=== "switch.py"
```python
--8<-- "docs/examples/widgets/switch.py"
```
=== "switch.css"
```sass
--8<-- "docs/examples/widgets/switch.css"
```
## Reactive Attributes
| Name | Type | Default | Description |
|---------|--------|---------|----------------------------------|
| `value` | `bool` | `False` | The default value of the switch. |
## Bindings
The switch widget defines directly the following bindings:
::: textual.widgets.Switch.BINDINGS
options:
show_root_heading: false
show_root_toc_entry: false
## Component Classes
The switch widget provides the following component classes:
::: textual.widgets.Switch.COMPONENT_CLASSES
options:
show_root_heading: false
show_root_toc_entry: false
## Messages
### ::: 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

View File

@@ -126,7 +126,6 @@ nav:
- "styles/width.md"
- Widgets:
- "widgets/button.md"
- "widgets/checkbox.md"
- "widgets/data_table.md"
- "widgets/directory_tree.md"
- "widgets/footer.md"
@@ -138,6 +137,7 @@ nav:
- "widgets/list_view.md"
- "widgets/placeholder.md"
- "widgets/static.md"
- "widgets/switch.md"
- "widgets/text_log.md"
- "widgets/tree.md"
- API:
@@ -145,7 +145,6 @@ nav:
- "api/app.md"
- "api/binding.md"
- "api/button.md"
- "api/checkbox.md"
- "api/color.md"
- "api/containers.md"
- "api/coordinate.md"
@@ -170,6 +169,7 @@ nav:
- "api/scroll_view.md"
- "api/static.md"
- "api/strip.md"
- "api/switch.md"
- "api/text_log.md"
- "api/timer.md"
- "api/tree.md"

View File

@@ -119,7 +119,7 @@ DarkSwitch .label {
color: $text-muted;
}
DarkSwitch Checkbox {
DarkSwitch Switch {
background: $boost;
dock: left;
}

View File

@@ -18,12 +18,12 @@ from textual.containers import Container, Horizontal
from textual.reactive import reactive
from textual.widgets import (
Button,
Checkbox,
DataTable,
Footer,
Header,
Input,
Static,
Switch,
TextLog,
)
@@ -138,7 +138,7 @@ Build your own or use the builtin widgets.
- **Input** Text / Password input.
- **Button** Clickable button with a number of styles.
- **Checkbox** A checkbox to toggle between states.
- **Switch** A switch to toggle between states.
- **DataTable** A spreadsheet-like widget for navigating data. Cells may contain text or Rich renderables.
- **Tree** An generic tree with expandable nodes.
- **DirectoryTree** A tree of file and folders.
@@ -199,16 +199,16 @@ class Title(Static):
class DarkSwitch(Horizontal):
def compose(self) -> ComposeResult:
yield Checkbox(value=self.app.dark)
yield Switch(value=self.app.dark)
yield Static("Dark mode toggle", classes="label")
def on_mount(self) -> None:
self.watch(self.app, "dark", self.on_dark_change, init=False)
def on_dark_change(self, dark: bool) -> None:
self.query_one(Checkbox).value = self.app.dark
self.query_one(Switch).value = self.app.dark
def on_checkbox_changed(self, event: Checkbox.Changed) -> None:
def on_switch_changed(self, event: Switch.Changed) -> None:
self.app.dark = event.value

View File

@@ -11,7 +11,6 @@ from ..case import camel_to_snake
if typing.TYPE_CHECKING:
from ..widget import Widget
from ._button import Button
from ._checkbox import Checkbox
from ._data_table import DataTable
from ._directory_tree import DirectoryTree
from ._footer import Footer
@@ -23,6 +22,7 @@ if typing.TYPE_CHECKING:
from ._placeholder import Placeholder
from ._pretty import Pretty
from ._static import Static
from ._switch import Switch
from ._text_log import TextLog
from ._tree import Tree
from ._welcome import Welcome
@@ -30,7 +30,7 @@ if typing.TYPE_CHECKING:
__all__ = [
"Button",
"Checkbox",
"Switch",
"DataTable",
"DirectoryTree",
"Footer",

View File

@@ -1,6 +1,5 @@
# This stub file must re-export every classes exposed in the __init__.py's `__all__` list:
from ._button import Button as Button
from ._checkbox import Checkbox as Checkbox
from ._data_table import DataTable as DataTable
from ._directory_tree import DirectoryTree as DirectoryTree
from ._footer import Footer as Footer
@@ -12,6 +11,7 @@ from ._list_view import ListView as ListView
from ._placeholder import Placeholder as Placeholder
from ._pretty import Pretty as Pretty
from ._static import Static as Static
from ._switch import Switch as Switch
from ._text_log import TextLog as TextLog
from ._tree import Tree as Tree
from ._welcome import Welcome as Welcome

View File

@@ -12,12 +12,12 @@ from ..scrollbar import ScrollBarRender
from ..widget import Widget
class Checkbox(Widget, can_focus=True):
"""A checkbox widget that represents a boolean value.
class Switch(Widget, can_focus=True):
"""A switch widget that represents a boolean value.
Can be toggled by clicking on it or through its [bindings][textual.widgets.Checkbox.BINDINGS].
Can be toggled by clicking on it or through its [bindings][textual.widgets.Switch.BINDINGS].
The checkbox widget also contains [component classes][textual.widgets.Checkbox.COMPONENT_CLASSES]
The switch widget also contains [component classes][textual.widgets.Switch.COMPONENT_CLASSES]
that enable more customization.
"""
@@ -27,20 +27,20 @@ class Checkbox(Widget, can_focus=True):
"""
| Key(s) | Description |
| :- | :- |
| enter,space | Toggle the checkbox status. |
| enter,space | Toggle the switch state. |
"""
COMPONENT_CLASSES: ClassVar[set[str]] = {
"checkbox--switch",
"switch--switch",
}
"""
| Class | Description |
| :- | :- |
| `checkbox--switch` | Targets the switch of the checkbox. |
| `switch--switch` | Targets the switch of the switch. |
"""
DEFAULT_CSS = """
Checkbox {
Switch {
border: tall transparent;
background: $panel;
height: auto;
@@ -48,49 +48,49 @@ class Checkbox(Widget, can_focus=True):
padding: 0 2;
}
Checkbox > .checkbox--switch {
Switch > .switch--switch {
background: $panel-darken-2;
color: $panel-lighten-2;
}
Checkbox:hover {
Switch:hover {
border: tall $background;
}
Checkbox:focus {
Switch:focus {
border: tall $accent;
}
Checkbox.-on {
Switch.-on {
}
Checkbox.-on > .checkbox--switch {
Switch.-on > .switch--switch {
color: $success;
}
"""
value = reactive(False, init=False)
"""The value of the checkbox; `True` for on and `False` for off."""
"""The value of the switch; `True` for on and `False` for off."""
slider_pos = reactive(0.0)
"""The position of the slider."""
class Changed(Message, bubble=True):
"""Posted when the status of the checkbox changes.
"""Posted when the status of the switch changes.
Can be handled using `on_checkbox_changed` in a subclass of `Checkbox`
Can be handled using `on_switch_changed` in a subclass of `Switch`
or in a parent widget in the DOM.
Attributes:
value: The value that the checkbox was changed to.
input: The `Checkbox` widget that was changed.
value: The value that the switch was changed to.
input: The `Switch` widget that was changed.
"""
def __init__(self, sender: Checkbox, value: bool) -> None:
def __init__(self, sender: Switch, value: bool) -> None:
super().__init__(sender)
self.value: bool = value
self.input: Checkbox = sender
self.input: Switch = sender
def __init__(
self,
@@ -101,14 +101,14 @@ class Checkbox(Widget, can_focus=True):
id: str | None = None,
classes: str | None = None,
):
"""Initialise the checkbox.
"""Initialise the switch.
Args:
value: The initial value of the checkbox. Defaults to False.
animate: True if the checkbox should animate when toggled. Defaults to True.
name: The name of the checkbox.
id: The ID of the checkbox in the DOM.
classes: The CSS classes of the checkbox.
value: The initial value of the switch. Defaults to False.
animate: True if the switch should animate when toggled. Defaults to True.
name: The name of the switch.
id: The ID of the switch in the DOM.
classes: The CSS classes of the switch.
"""
super().__init__(name=name, id=id, classes=classes)
if value:
@@ -128,7 +128,7 @@ class Checkbox(Widget, can_focus=True):
self.set_class(slider_pos == 1, "-on")
def render(self) -> RenderableType:
style = self.get_component_rich_style("checkbox--switch")
style = self.get_component_rich_style("switch--switch")
return ScrollBarRender(
virtual_size=100,
window_size=50,
@@ -150,6 +150,6 @@ class Checkbox(Widget, can_focus=True):
self.toggle()
def toggle(self) -> None:
"""Toggle the checkbox value. As a result of the value changing,
a Checkbox.Changed message will be posted."""
"""Toggle the switch value. As a result of the value changing,
a Switch.Changed message will be posted."""
self.value = not self.value

File diff suppressed because one or more lines are too long

View File

@@ -52,8 +52,8 @@ def test_dock_layout_sidebar(snap_compare):
# from these examples which test rendering and simple interactions with it.
def test_checkboxes(snap_compare):
"""Tests checkboxes but also acts a regression test for using
def test_switches(snap_compare):
"""Tests switches but also acts a regression test for using
width: auto in a Horizontal layout context."""
press = [
"shift+tab",
@@ -63,7 +63,7 @@ def test_checkboxes(snap_compare):
"enter", # toggle on
"wait:20",
]
assert snap_compare(WIDGET_EXAMPLES_DIR / "checkbox.py", press=press)
assert snap_compare(WIDGET_EXAMPLES_DIR / "switch.py", press=press)
def test_input_and_focus(snap_compare):

View File

@@ -152,21 +152,21 @@ def test_focus_next_and_previous_with_type_selector_without_self():
screen = app.screen
from textual.containers import Horizontal, Vertical
from textual.widgets import Button, Checkbox, Input
from textual.widgets import Button, Input, Switch
screen._add_children(
Vertical(
Horizontal(
Input(id="w3"),
Checkbox(id="w4"),
Switch(id="w4"),
Input(id="w5"),
Button(id="w6"),
Checkbox(id="w7"),
Switch(id="w7"),
id="w2",
),
Horizontal(
Button(id="w9"),
Checkbox(id="w10"),
Switch(id="w10"),
Button(id="w11"),
Input(id="w12"),
Input(id="w13"),
@@ -180,11 +180,11 @@ def test_focus_next_and_previous_with_type_selector_without_self():
assert screen.focused.id == "w3"
assert screen.focus_next(Button).id == "w6"
assert screen.focus_next(Checkbox).id == "w7"
assert screen.focus_next(Switch).id == "w7"
assert screen.focus_next(Input).id == "w12"
assert screen.focus_previous(Button).id == "w11"
assert screen.focus_previous(Checkbox).id == "w10"
assert screen.focus_previous(Switch).id == "w10"
assert screen.focus_previous(Button).id == "w9"
assert screen.focus_previous(Input).id == "w5"