Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Checkbox, RadioButton and RadioSet #1872

Merged
merged 113 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from 107 commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
6982805
Initial planning of the class hierarchy for toggle widgets
davep Feb 15, 2023
2ee1a24
Add a couple of missing types
davep Feb 16, 2023
22a4772
Add return information to the render docstring
davep Feb 16, 2023
de04be1
Add a toggle method
davep Feb 16, 2023
279e2fc
Improve the types of the parts that make up the renderable
davep Feb 16, 2023
9053f73
Move Checkbox and RadioButton out into their own files
davep Feb 16, 2023
d4b61e2
Give the Checkbox a docstring
davep Feb 16, 2023
c114c87
Add a placeholder docstring to the radio button
davep Feb 16, 2023
9dea934
Add some default styling to the toggle button classes
davep Feb 16, 2023
47550a6
Allow placing the label before the button
davep Feb 16, 2023
ae7f1b4
Simplify the rendering
davep Feb 16, 2023
5c9ed14
Add a reminder about handling a disabled keyword
davep Feb 16, 2023
a6389f1
Add a base changed message for toggle boxes
davep Feb 16, 2023
5e3ed65
Add a Checkbox-specific Changed message
davep Feb 16, 2023
3df4634
Add a RadioButtons-specific Changed message
davep Feb 16, 2023
41ce300
Add notes about the desire to not hard-code the namespaces
davep Feb 16, 2023
6f4dde4
Merge branch 'main' into toggle-boxen
davep Feb 20, 2023
7da9cbe
Add some initial component classes
davep Feb 20, 2023
fb32e53
Swap the checkbox to using an actual check mark
davep Feb 20, 2023
122457a
Swap the radio button to using a circular character for the inner
davep Feb 20, 2023
bf727fc
Add a selected message to the toggle button
davep Feb 20, 2023
594c1fa
Start fleshing out a RadioSet widget
davep Feb 20, 2023
cd3a47b
Swap to using a button change event for radio switching
davep Feb 20, 2023
f97194a
Add missing type annotation
davep Feb 20, 2023
9e6cb76
Add missing type annotation
davep Feb 20, 2023
791bf12
Add missing type annotation
davep Feb 20, 2023
6eb2fde
Add the usual set of widget parameters to the RadioSet
davep Feb 20, 2023
063e815
Add a preflight check that all buttons are of a type we can handle
davep Feb 20, 2023
54d59ef
Further explain why we keep an internal list of buttons
davep Feb 20, 2023
8d63af1
Ensure a button is pressed on startup
davep Feb 20, 2023
fe1261c
Tidy up a comment
davep Feb 20, 2023
3ee35f6
Add properties for getting the current pressed button
davep Feb 20, 2023
adca897
Add a RadioSet.Changed message
davep Feb 20, 2023
ef0ef58
Merge branch 'main' into toggle-boxen
davep Feb 21, 2023
0a2781f
Merge branch 'main' into toggle-boxen
davep Feb 21, 2023
ef70ef9
Add support for disabled on the checkbox and radioset
davep Feb 21, 2023
3d759d6
WiP revamping of how checkboxes and radio buttons look
davep Feb 21, 2023
6c3232e
Don't allow a radio button to be turned off
davep Feb 21, 2023
461d628
Start toggling an on class on the toggle boxes
davep Feb 21, 2023
d5f4ae1
Have the radio set work more around DOM queries
davep Feb 21, 2023
ec5d566
Drop support for a Selected message on the toggle boxes
davep Feb 21, 2023
621f542
Remove unnecessary import
davep Feb 21, 2023
526e7a9
Remove unnecessary import
davep Feb 21, 2023
f94313f
Drop support for individual on/off inner characters for the buttons
davep Feb 21, 2023
ec698bc
Simplify the naming of the component classes
davep Feb 21, 2023
cbd0cfd
Merge branch 'main' into toggle-boxen
davep Feb 22, 2023
f8f3691
Add required import
davep Feb 22, 2023
277412b
Simplify the styling of the button
davep Feb 22, 2023
ada24c9
Simplify the setting of the button characters
davep Feb 22, 2023
5c89efc
Simplify the setting and handling of the label
davep Feb 22, 2023
724fef7
Sort the list of component classes in the docstring
davep Feb 22, 2023
8506f79
Simplify the setting of the button characters
davep Feb 22, 2023
5743148
Fix -light-mode not being set on startup if not in dark mode
davep Feb 22, 2023
23cc1da
Bring the button_first setting internal
davep Feb 22, 2023
7ee970e
Support richer labels
davep Feb 22, 2023
8bcee3f
More style changing to the toggle buttons
davep Feb 22, 2023
0358d74
Tweak the buttons in light mode
davep Feb 22, 2023
967773f
More tidying of the button styles
davep Feb 22, 2023
9f9fe6f
Docstring tidy
davep Feb 22, 2023
18705b4
Merge branch 'main' into toggle-boxen
davep Feb 22, 2023
9dd66fc
Tidy some documentation in the base toggle box class
davep Feb 22, 2023
91199fd
Make the label available but read-only
davep Feb 22, 2023
1b2f5a1
Stop wrapping the spacer as Text
davep Feb 22, 2023
789e051
Don't react on initial set
davep Feb 22, 2023
177aae6
Don't force a radio set to have at least one button on on start
davep Feb 22, 2023
a32a9fa
Reduce the calls to Text when building the button
davep Feb 23, 2023
2fbb64c
Add methods for getting the width and height of the widget
davep Feb 23, 2023
46018c3
Add missing import
davep Feb 23, 2023
73cb4c5
Turn off wrapping and add ellipsis to the overall widget
davep Feb 23, 2023
ceade8a
Ensure we're only working with the first line of the label
davep Feb 23, 2023
af3c5f1
Ensure that toggle buttons are width auto
davep Feb 23, 2023
1c05f79
Add some default styling to a radio set
davep Feb 23, 2023
99341a9
Rename the file that holds ToggleButton
davep Feb 23, 2023
f52aac4
Initial work on adding toggle buttons to the documentation
davep Feb 23, 2023
affb3b6
Type-tidy the radio set event handling
davep Feb 23, 2023
652d449
Type-tidy the radio set button query
davep Feb 23, 2023
6233ae3
Extend and improve the RadioSet docstrings
davep Feb 23, 2023
66af586
Add a missing
davep Feb 23, 2023
3fd3d27
Add the widget reference for Checkbox
davep Feb 23, 2023
4f75a39
Fix copy/paste-o in the checkbox reference
davep Feb 23, 2023
01215a1
Tweak the style of the box in the example for checkbox
davep Feb 23, 2023
87bd610
Swap the checkbox example to the newer compose approach
davep Feb 23, 2023
a3d50d9
Revert example code to composing the old way
davep Feb 23, 2023
a821b2f
Add width reference documentation for radio buttons
davep Feb 23, 2023
316faf9
Add width reference documentation for radio set
davep Feb 23, 2023
307b178
Give radio button its own example code now
davep Feb 23, 2023
837d56f
Provide a default label for the toggle buttons
davep Feb 23, 2023
0520856
Prevent the initial Changed event when initial value is True
davep Feb 23, 2023
cfe7be3
Some initial Checkbox unit testing
davep Feb 23, 2023
7c6522e
Add initial radio button tests
davep Feb 23, 2023
2362662
Add CSS class tests for check boxes and radio buttons
davep Feb 23, 2023
d341b8b
Fix a couple of copy/paste-o docstrings
davep Feb 23, 2023
8d726fb
Ensure the pressed button index is correct in the message
davep Feb 23, 2023
719c831
Add some unit testing for RadioSet
davep Feb 23, 2023
24ce819
Add snapshot tests for the toggle button examples
davep Feb 23, 2023
f0a6771
Update the ChangeLog and the roadmap
davep Feb 23, 2023
e6e46d0
Merge branch 'main' into toggle-boxen
davep Feb 27, 2023
3de871f
Use prevent to stop the Changed event on initial value setting
davep Feb 27, 2023
f436489
Rework the layout of the radio set example
davep Feb 27, 2023
23c0587
Rework the layout of the radio button example
davep Feb 27, 2023
6d0fc04
Rework the layout of the checkbox example
davep Feb 27, 2023
17bc19e
Ensure multiple buttons can't be on on startup
davep Feb 27, 2023
7f90e4e
Add a test for adding multiple on buttons to a radioset
davep Feb 27, 2023
db9b9e1
Fix a typo
davep Feb 27, 2023
445e29f
Improve the wording of a test description
davep Feb 27, 2023
7bfed01
Improve the wording of a test description
davep Feb 27, 2023
8c5fb38
Merge branch 'main' into toggle-boxen
davep Feb 27, 2023
b5c028c
Fix a docstring typo
davep Feb 27, 2023
dec9a77
Fix a docstring typo
davep Feb 27, 2023
47eb9f0
Typo fix
davep Feb 27, 2023
201ab4e
Move RadioSet.Changed near the top of the class
davep Feb 27, 2023
d8ae11d
Drop a vestigial cast
davep Feb 27, 2023
df6f7bd
Don't bubble up any Changed event when stopping all-off on radio set
davep Feb 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased

### Added

- Added `Checkbox` https://github.com/Textualize/textual/pull/1872
- Added `RadioButton` https://github.com/Textualize/textual/pull/1872
- Added `RadioSet` https://github.com/Textualize/textual/pull/1872

### Fixed

- Fix exceptions in watch methods being hidden on startup https://github.com/Textualize/textual/issues/1886
Expand Down
1 change: 1 addition & 0 deletions docs/api/checkbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: textual.widgets.Checkbox
1 change: 1 addition & 0 deletions docs/api/radiobutton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: textual.widgets.RadioButton
1 change: 1 addition & 0 deletions docs/api/radioset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: textual.widgets.RadioSet
1 change: 1 addition & 0 deletions docs/api/toggle_button.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: textual.widgets._toggle_button.ToggleButton
11 changes: 11 additions & 0 deletions docs/examples/widgets/checkbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Screen {
align: center middle;
}

Vertical {
width: auto;
height: auto;
border: solid $primary;
background: $panel;
padding: 2;
}
25 changes: 25 additions & 0 deletions docs/examples/widgets/checkbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from textual.app import App, ComposeResult
from textual.containers import Vertical
from textual.widgets import Checkbox


class CheckboxApp(App[None]):
CSS_PATH = "checkbox.css"

def compose(self) -> ComposeResult:
with Vertical():
yield Checkbox("Arrakis :sweat:")
yield Checkbox("Caladan")
yield Checkbox("Chusuk")
yield Checkbox("[b]Giedi Prime[/b]")
yield Checkbox("[magenta]Ginaz[/]")
yield Checkbox("Grumman", True)
yield Checkbox("Kaitain", id="initial_focus")
yield Checkbox("Novebruns", True)

def on_mount(self):
self.query_one("#initial_focus", Checkbox).focus()


if __name__ == "__main__":
CheckboxApp().run()
7 changes: 7 additions & 0 deletions docs/examples/widgets/radio_button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Screen {
align: center middle;
}

RadioSet {
width: 50%;
}
27 changes: 27 additions & 0 deletions docs/examples/widgets/radio_button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from textual.app import App, ComposeResult
from textual.widgets import RadioButton, RadioSet


class RadioChoicesApp(App[None]):
CSS_PATH = "radio_button.css"

def compose(self) -> ComposeResult:
with RadioSet():
yield RadioButton("Battlestar Galactica")
yield RadioButton("Dune 1984")
yield RadioButton("Dune 2021", id="focus_me")
yield RadioButton("Serenity", value=True)
yield RadioButton("Star Trek: The Motion Picture")
yield RadioButton("Star Wars: A New Hope")
yield RadioButton("The Last Starfighter")
yield RadioButton(
"Total Recall :backhand_index_pointing_right: :red_circle:"
)
yield RadioButton("Wing Commander")

def on_mount(self) -> None:
self.query_one("#focus_me", RadioButton).focus()


if __name__ == "__main__":
RadioChoicesApp().run()
12 changes: 12 additions & 0 deletions docs/examples/widgets/radio_set.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Screen {
align: center middle;
}

Horizontal {
align: center middle;
height: auto;
}

RadioSet {
width: 45%;
}
43 changes: 43 additions & 0 deletions docs/examples/widgets/radio_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from textual.app import App, ComposeResult
from textual.containers import Horizontal
from textual.widgets import RadioButton, RadioSet


class RadioChoicesApp(App[None]):
CSS_PATH = "radio_set.css"

def compose(self) -> ComposeResult:
with Horizontal():
# A RadioSet built up from RadioButtons.
with RadioSet():
yield RadioButton("Battlestar Galactica")
yield RadioButton("Dune 1984")
yield RadioButton("Dune 2021")
yield RadioButton("Serenity", value=True)
yield RadioButton("Star Trek: The Motion Picture")
yield RadioButton("Star Wars: A New Hope")
yield RadioButton("The Last Starfighter")
yield RadioButton(
"Total Recall :backhand_index_pointing_right: :red_circle:",
id="focus_me",
)
yield RadioButton("Wing Commander")
# A RadioSet built up from a collection of strings.
yield RadioSet(
"Amanda",
"Connor MacLeod",
"Duncan MacLeod",
"Heather MacLeod",
"Joe Dawson",
"Kurgan, [bold italic red]The[/]",
"Methos",
"Rachel Ellenstein",
"Ramírez",
)

def on_mount(self) -> None:
self.query_one("#focus_me", RadioButton).focus()


if __name__ == "__main__":
RadioChoicesApp().run()
4 changes: 2 additions & 2 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
- [ ] Checkbox
- [X] Checkbox
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such meta. Wow.

- [ ] Content switcher
- [x] DataTable
* [x] Cell select
Expand Down Expand Up @@ -70,7 +70,7 @@ Widgets are key to making user-friendly interfaces. The builtin widgets should c
* [ ] Candlestick chars
- [ ] Progress bars
* [ ] Style variants (solid, thin etc)
- [ ] Radio boxes
- [X] Radio boxes
- [ ] Spark-lines
- [X] Switch
- [ ] Tabs
Expand Down
59 changes: 59 additions & 0 deletions docs/widgets/checkbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Checkbox

A simple checkbox width which stores a boolean value.
davep marked this conversation as resolved.
Show resolved Hide resolved

- [x] Focusable
- [ ] Container

## Example

The example below shows check boxes 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 value of the checkbox. |

## Bindings

The checkbox widget defines directly the following bindings:

::: textual.widgets._toggle_button.ToggleButton.BINDINGS
options:
show_root_heading: false
show_root_toc_entry: false

## Component Classes

The checkbox widget provides the following component classes:

::: textual.widgets._toggle_button.ToggleButton.COMPONENT_CLASSES
options:
show_root_heading: false
show_root_toc_entry: false

## Messages

### ::: textual.widgets.Checkbox.Changed

## See Also

- [Checkbox](../api/checkbox.md) code reference
62 changes: 62 additions & 0 deletions docs/widgets/radiobutton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# RadioButton

A simple radio button which stores a boolean value.

- [x] Focusable
- [ ] Container

A radio button is best used with others inside a [`RadioSet`](./radioset.md).

## Example

The example below shows radio buttons, used within a [`RadioSet`](./radioset.md).

=== "Output"

```{.textual path="docs/examples/widgets/radio_button.py"}
```

=== "radio_button.py"

```python
--8<-- "docs/examples/widgets/radio_button.py"
```

=== "radio_button.css"

```sass
--8<-- "docs/examples/widgets/radio_button.css"
```

## Reactive Attributes

| Name | Type | Default | Description |
|---------|--------|---------|--------------------------------|
| `value` | `bool` | `False` | The value of the radio button. |

## Bindings

The radio button widget defines directly the following bindings:

::: textual.widgets._toggle_button.ToggleButton.BINDINGS
options:
show_root_heading: false
show_root_toc_entry: false

## Component Classes

The radio button widget provides the following component classes:

::: textual.widgets._toggle_button.ToggleButton.COMPONENT_CLASSES
options:
show_root_heading: false
show_root_toc_entry: false

## Messages

### ::: textual.widgets.RadioButton.Changed

## See Also

- [RadioButton](../api/radiobutton.md) code reference
- [RadioSet](./radioset.md)
37 changes: 37 additions & 0 deletions docs/widgets/radioset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# RadioSet

A container widget that groups [`RadioButton`](./radiobutton.md)s together.

- [ ] Focusable
- [x] Container

## Example

The example below shows two radio sets, one built using a collection of
[radio buttons](./radiobutton.md), the other a collection of simple strings.

=== "Output"

```{.textual path="docs/examples/widgets/radio_set.py"}
```

=== "radio_set.py"

```python
--8<-- "docs/examples/widgets/radio_set.py"
```

=== "radio_set.css"

```sass
--8<-- "docs/examples/widgets/radio_set.css"
```

## Messages

### ::: textual.widgets.RadioSet.Changed

## See Also

- [RadioSet](../api/radioset.md) code reference
- [RadioButton](./radiobutton.md)
7 changes: 7 additions & 0 deletions mkdocs-nav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ nav:
- "styles/width.md"
- Widgets:
- "widgets/button.md"
- "widgets/checkbox.md"
- "widgets/data_table.md"
- "widgets/directory_tree.md"
- "widgets/footer.md"
Expand All @@ -133,6 +134,8 @@ nav:
- "widgets/markdown_viewer.md"
- "widgets/markdown.md"
- "widgets/placeholder.md"
- "widgets/radiobutton.md"
- "widgets/radioset.md"
- "widgets/static.md"
- "widgets/switch.md"
- "widgets/text_log.md"
Expand All @@ -141,6 +144,7 @@ nav:
- "api/app.md"
- "api/binding.md"
- "api/button.md"
- "api/checkbox.md"
- "api/color.md"
- "api/containers.md"
- "api/coordinate.md"
Expand All @@ -163,13 +167,16 @@ nav:
- "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/text_log.md"
- "api/toggle_button.md"
- "api/timer.md"
- "api/tree_node.md"
- "api/tree.md"
Expand Down
1 change: 1 addition & 0 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ def __init__(
self._dom_ready = False
self._batch_count = 0
self.set_class(self.dark, "-dark-mode")
self.set_class(not self.dark, "-light-mode")

@property
def return_value(self) -> ReturnType | None:
Expand Down
Loading