mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Cleanup easing example
This commit is contained in:
@@ -5,10 +5,14 @@ from textual.widgets import Static
|
|||||||
|
|
||||||
|
|
||||||
class JustABox(App):
|
class JustABox(App):
|
||||||
|
|
||||||
|
css_path = "../darren/just_a_box.css"
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Static("Hello, world!", classes="box1")
|
yield Static("Hello, world!", classes="box1")
|
||||||
|
|
||||||
|
|
||||||
|
app = JustABox(watch_css=True)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = JustABox(css_path="../darren/just_a_box.css", watch_css=True)
|
|
||||||
app.run()
|
app.run()
|
||||||
|
|||||||
@@ -3,6 +3,38 @@ EasingButtons > Button {
|
|||||||
}
|
}
|
||||||
EasingButtons {
|
EasingButtons {
|
||||||
dock: left;
|
dock: left;
|
||||||
width: 20;
|
|
||||||
overflow: auto auto;
|
overflow: auto auto;
|
||||||
|
width: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bar-container {
|
||||||
|
content-align: center middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#duration-input {
|
||||||
|
width: 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inputs {
|
||||||
|
padding: 1;
|
||||||
|
height: auto;
|
||||||
|
dock: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bar {
|
||||||
|
width: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#other {
|
||||||
|
width: 1fr;
|
||||||
|
background: #555555;
|
||||||
|
padding: 1;
|
||||||
|
height: 100%;
|
||||||
|
border-left: vkey $background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#opacity-widget {
|
||||||
|
padding: 1;
|
||||||
|
background: $panel;
|
||||||
|
border: wide #969696;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,110 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from rich.console import RenderableType
|
||||||
|
|
||||||
|
from textual import layout
|
||||||
from textual._easing import EASING
|
from textual._easing import EASING
|
||||||
from textual.app import ComposeResult, App
|
from textual.app import ComposeResult, App
|
||||||
|
from textual.cli.previews.borders import TEXT
|
||||||
|
from textual.reactive import Reactive
|
||||||
|
from textual.scrollbar import ScrollBarRender
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
from textual.widgets import Button, Static
|
from textual.widgets import Button, Static, Footer
|
||||||
|
from textual.widgets.text_input import TextWidgetBase, TextInput
|
||||||
|
|
||||||
|
VIRTUAL_SIZE = 100
|
||||||
|
WINDOW_SIZE = 10
|
||||||
|
START_POSITION = 0.0
|
||||||
|
END_POSITION = float(VIRTUAL_SIZE - WINDOW_SIZE)
|
||||||
|
|
||||||
|
|
||||||
class EasingButtons(Widget):
|
class EasingButtons(Widget):
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
for easing in EASING:
|
for easing in sorted([e for e in EASING if e != "round"], reverse=True):
|
||||||
yield Button(easing)
|
yield Button(easing, id=easing)
|
||||||
|
|
||||||
|
|
||||||
|
class Bar(Widget):
|
||||||
|
position = Reactive.init(START_POSITION)
|
||||||
|
animation_running = Reactive(False)
|
||||||
|
|
||||||
|
def render(self) -> RenderableType:
|
||||||
|
return ScrollBarRender(
|
||||||
|
virtual_size=VIRTUAL_SIZE,
|
||||||
|
window_size=WINDOW_SIZE,
|
||||||
|
position=self.position,
|
||||||
|
style="green" if self.animation_running else "red",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EasingApp(App):
|
class EasingApp(App):
|
||||||
|
position = Reactive.init(START_POSITION)
|
||||||
|
duration = Reactive.var(1.0)
|
||||||
|
|
||||||
|
def on_load(self):
|
||||||
|
self.bind(
|
||||||
|
"ctrl+p", "focus('duration-input')", description="Focus: Duration Input"
|
||||||
|
)
|
||||||
|
self.bind("ctrl+b", "toggle_dark", description="Toggle Dark")
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
|
self.animated_bar = Bar()
|
||||||
|
self.animated_bar.position = START_POSITION
|
||||||
|
duration_input = TextInput(
|
||||||
|
placeholder="Duration", initial="1.0", id="duration-input"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.opacity_widget = Static(
|
||||||
|
f"[b]Welcome to Textual![/]\n\n{TEXT}", id="opacity-widget"
|
||||||
|
)
|
||||||
|
|
||||||
yield EasingButtons()
|
yield EasingButtons()
|
||||||
self.text = Static("Easing examples")
|
yield layout.Vertical(
|
||||||
yield self.text
|
layout.Vertical(Static("Animation Duration:"), duration_input, id="inputs"),
|
||||||
|
layout.Horizontal(
|
||||||
|
self.animated_bar,
|
||||||
|
layout.Container(self.opacity_widget, id="other"),
|
||||||
|
),
|
||||||
|
Footer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
self.animated_bar.animation_running = True
|
||||||
|
|
||||||
|
def _animation_complete():
|
||||||
|
self.animated_bar.animation_running = False
|
||||||
|
|
||||||
|
target_position = (
|
||||||
|
END_POSITION if self.position == START_POSITION else START_POSITION
|
||||||
|
)
|
||||||
|
self.animate(
|
||||||
|
"position",
|
||||||
|
value=target_position,
|
||||||
|
final_value=target_position,
|
||||||
|
duration=self.duration,
|
||||||
|
easing=event.button.id,
|
||||||
|
on_complete=_animation_complete,
|
||||||
|
)
|
||||||
|
|
||||||
|
def watch_position(self, value: int):
|
||||||
|
self.animated_bar.position = value
|
||||||
|
self.opacity_widget.styles.opacity = 1 - value / END_POSITION
|
||||||
|
|
||||||
|
def on_text_widget_base_changed(self, event: TextWidgetBase.Changed):
|
||||||
|
if event.sender.id == "duration-input":
|
||||||
|
new_duration = _try_float(event.value)
|
||||||
|
if new_duration is not None:
|
||||||
|
self.duration = new_duration
|
||||||
|
|
||||||
|
def action_toggle_dark(self):
|
||||||
|
self.dark = not self.dark
|
||||||
|
|
||||||
|
|
||||||
|
def _try_float(string: str) -> float | None:
|
||||||
|
try:
|
||||||
|
return float(string)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
app = EasingApp(css_path="easing.css", watch_css=True)
|
app = EasingApp(css_path="easing.css", watch_css=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user