-
Hello there, I'm trying to build a widget that can switch between its contents e.g: from textual.app import App, ComposeResult
from textual.widget import Widget
from textual.reactive import Reactive
from textual.widgets import Input, Footer
class TwoInOne(Widget):
toggleSwitch = Reactive(False)
DEFAULT_CSS = """
TwoInOne {
dock: bottom;
}
"""
def __init__(self):
super().__init__()
self.footer = Footer()
self.inpt = Input()
def render(self):
return self.footer if self.toggleSwitch else self.inpt
class Example(App):
def compose(self) -> ComposeResult:
yield TwoInOne()
if __name__ == "__main__":
Example().run() I was able to do something like this in the past versions of textual (when there wasn't a compose function) I would use self.mount and self.remove but remove is actually deleting the widget and then we back to the dynamic widgets idea any way I can get this working in the new version ? (0.4.0) |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
The Textual tutorial pretty much walks you through building a widget that's made of other widgets, as each "stopwatch" is made up of a number of other widgets. |
Beta Was this translation helpful? Give feedback.
-
Thank you for your answer! I was trying this approach before with add/remove_class functions, I wasn't aware of set_class, which is a more convenient function as it takes a boolean argument. it seems that I can't focus on other widget once I focused on Input for editing and that's why I was trying to avoid this approach. with your example above, I found out something interesting concerning the focus issue. from textual.app import App, ComposeResult
from textual.containers import Container
from textual.binding import Binding
from textual.widgets import Static, Input, Button, Header
from textual.reactive import reactive
class SwapContent(Container):
DEFAULT_CSS = """
SwapContent {
height: 5;
border: solid red;
background: #444;
}
.hidden {
display: none;
}
"""
allow_edit = reactive(False)
def compose(self) -> ComposeResult:
yield Static("This is just some static text", id="text")
yield Input(placeholder="This is an input field", id="input", classes="hidden")
def watch_allow_edit(self, allow_edit: bool) -> None:
self.query_one("#text", Static).set_class(allow_edit, "hidden")
self.query_one("#input", Input).set_class(not allow_edit, "hidden")
if allow_edit:
self.query_one("#input", Input).focus()
class SwapApp(App[None]):
BINDINGS = [
("i", "input", "Input"),
("q", "quit", "Quit"),
Binding("escape", "focus_reset", "Focus", False),
]
def compose(self) -> ComposeResult:
yield Header()
yield SwapContent()
yield Button("Toggle edit", id="toggle")
def action_input(self):
swapper = self.query_one(SwapContent)
swapper.allow_edit = True
def action_focus_reset(self):
swapper = self.query_one(SwapContent)
swapper.allow_edit = False
# self.query_one(Header).focus()
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "toggle":
swapper = self.query_one(SwapContent)
swapper.allow_edit = not swapper.allow_edit
if __name__ == "__main__":
SwapApp().run() pressing that's odd because the call of focus on the Header in |
Beta Was this translation helpful? Give feedback.
The Textual tutorial pretty much walks you through building a widget that's made of other widgets, as each "stopwatch" is made up of a number of other widgets.