mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'css' of github.com:Textualize/textual into easing-examples
This commit is contained in:
@@ -8,7 +8,7 @@ from textual.widgets import Placeholder
|
||||
|
||||
|
||||
class VerticalContainer(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
overflow: hidden auto;
|
||||
@@ -24,7 +24,7 @@ class VerticalContainer(Widget):
|
||||
|
||||
|
||||
class Introduction(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Introduction {
|
||||
background: indigo;
|
||||
color: white;
|
||||
|
||||
@@ -23,7 +23,9 @@ class ButtonsApp(App[str]):
|
||||
|
||||
|
||||
app = ButtonsApp(
|
||||
log_path="textual.log", css_path="buttons.css", watch_css=True, log_verbosity=2
|
||||
log_path="textual.log",
|
||||
css_path="buttons.css",
|
||||
watch_css=True,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -23,7 +23,7 @@ class ColorDisplay(Widget, can_focus=True):
|
||||
|
||||
|
||||
class ColorNames(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
ColorDisplay {
|
||||
height: 1;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ class ButtonsApp(App[str]):
|
||||
|
||||
|
||||
app = ButtonsApp(
|
||||
log_path="textual.log", css_path="buttons.css", watch_css=True, log_verbosity=2
|
||||
log_path="textual.log",
|
||||
css_path="buttons.css",
|
||||
watch_css=True,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -62,9 +62,7 @@ class FileSearchApp(App):
|
||||
self.file_table.filter = event.value
|
||||
|
||||
|
||||
app = FileSearchApp(
|
||||
log_path="textual.log", css_path="file_search.scss", watch_css=True, log_verbosity=2
|
||||
)
|
||||
app = FileSearchApp(css_path="file_search.scss", watch_css=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = app.run()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Screen {
|
||||
layout: dock;
|
||||
docks: top=top bottom=bottom;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#file_table_wrapper {
|
||||
|
||||
@@ -5,7 +5,7 @@ from textual.widget import Widget
|
||||
|
||||
class FiftyApp(App):
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Screen {
|
||||
layout: vertical;
|
||||
}
|
||||
@@ -24,6 +24,7 @@ class FiftyApp(App):
|
||||
yield layout.Horizontal(Widget(), Widget())
|
||||
yield layout.Horizontal(Widget(), Widget())
|
||||
|
||||
|
||||
app = FiftyApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
||||
@@ -61,9 +61,7 @@ class InputApp(App[str]):
|
||||
self.celsius.value = f"{celsius:.1f}"
|
||||
|
||||
|
||||
app = InputApp(
|
||||
log_path="textual.log", css_path="input.scss", watch_css=True, log_verbosity=2
|
||||
)
|
||||
app = InputApp(log_path="textual.log", css_path="input.scss", watch_css=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = app.run()
|
||||
|
||||
@@ -9,7 +9,7 @@ placeholders_count = 12
|
||||
|
||||
|
||||
class VerticalContainer(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
overflow: hidden auto;
|
||||
@@ -26,7 +26,7 @@ class VerticalContainer(Widget):
|
||||
|
||||
|
||||
class Introduction(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Introduction {
|
||||
background: indigo;
|
||||
color: white;
|
||||
|
||||
@@ -10,7 +10,7 @@ initial_placeholders_count = 4
|
||||
|
||||
|
||||
class VerticalContainer(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
overflow: hidden auto;
|
||||
@@ -30,7 +30,7 @@ class VerticalContainer(Widget):
|
||||
|
||||
|
||||
class Introduction(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Introduction {
|
||||
background: indigo;
|
||||
color: white;
|
||||
|
||||
@@ -11,7 +11,7 @@ class Thing(Static):
|
||||
|
||||
|
||||
class AddRemoveApp(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
#buttons {
|
||||
dock: top;
|
||||
height: auto;
|
||||
|
||||
@@ -3,14 +3,14 @@ Screen {
|
||||
}
|
||||
|
||||
#calculator {
|
||||
layout: table;
|
||||
table-size: 4;
|
||||
table-gutter: 1 2;
|
||||
table-columns: 1fr;
|
||||
table-rows: 2fr 1fr 1fr 1fr 1fr 1fr;
|
||||
layout: grid;
|
||||
grid-size: 4;
|
||||
grid-gutter: 1 2;
|
||||
grid-columns: 1fr;
|
||||
grid-rows: 2fr 1fr 1fr 1fr 1fr 1fr;
|
||||
margin: 1 2;
|
||||
min-height:26;
|
||||
min-width: 50;
|
||||
min-height:25;
|
||||
min-width: 26;
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -18,18 +18,15 @@ Button {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.display {
|
||||
#numbers {
|
||||
column-span: 4;
|
||||
content-align: right middle;
|
||||
padding: 0 1;
|
||||
height: 100%;
|
||||
background: $panel-darken-2;
|
||||
background: $primary-lighten-2;
|
||||
color: $text-primary-lighten-2;
|
||||
}
|
||||
|
||||
.special {
|
||||
tint: $text-panel 20%;
|
||||
}
|
||||
|
||||
.zero {
|
||||
#number-0 {
|
||||
column-span: 2;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,143 @@
|
||||
from textual.app import App
|
||||
from decimal import Decimal
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual import events
|
||||
from textual.layout import Container
|
||||
from textual.reactive import Reactive
|
||||
from textual.widgets import Button, Static
|
||||
|
||||
|
||||
class CalculatorApp(App):
|
||||
def compose(self):
|
||||
"""A working 'desktop' calculator."""
|
||||
|
||||
numbers = Reactive.var("0")
|
||||
show_ac = Reactive.var(True)
|
||||
left = Reactive.var(Decimal("0"))
|
||||
right = Reactive.var(Decimal("0"))
|
||||
value = Reactive.var("")
|
||||
operator = Reactive.var("plus")
|
||||
|
||||
KEY_MAP = {
|
||||
"+": "plus",
|
||||
"-": "minus",
|
||||
".": "point",
|
||||
"*": "multiply",
|
||||
"/": "divide",
|
||||
"_": "plus-minus",
|
||||
"%": "percent",
|
||||
"=": "equals",
|
||||
}
|
||||
|
||||
def watch_numbers(self, value: str) -> None:
|
||||
"""Called when numbers is updated."""
|
||||
# Update the Numbers widget
|
||||
self.query_one("#numbers", Static).update(value)
|
||||
|
||||
def compute_show_ac(self) -> bool:
|
||||
"""Compute switch to show AC or C button"""
|
||||
return self.value in ("", "0") and self.numbers == "0"
|
||||
|
||||
def watch_show_ac(self, show_ac: bool) -> None:
|
||||
"""Called when show_ac changes."""
|
||||
self.query_one("#c").display = not show_ac
|
||||
self.query_one("#ac").display = show_ac
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
"""Add our buttons."""
|
||||
yield Container(
|
||||
Static("0", classes="display"),
|
||||
Button("AC", classes="special"),
|
||||
Button("+/-", classes="special"),
|
||||
Button("%", classes="special"),
|
||||
Button("÷", variant="warning"),
|
||||
Button("7"),
|
||||
Button("8"),
|
||||
Button("9"),
|
||||
Button("×", variant="warning"),
|
||||
Button("4"),
|
||||
Button("5"),
|
||||
Button("6"),
|
||||
Button("-", variant="warning"),
|
||||
Button("1"),
|
||||
Button("2"),
|
||||
Button("3"),
|
||||
Button("+", variant="warning"),
|
||||
Button("0", classes="operator zero"),
|
||||
Button("."),
|
||||
Button("=", variant="warning"),
|
||||
Static(id="numbers"),
|
||||
Button("AC", id="ac", variant="primary"),
|
||||
Button("C", id="c", variant="primary"),
|
||||
Button("+/-", id="plus-minus", variant="primary"),
|
||||
Button("%", id="percent", variant="primary"),
|
||||
Button("÷", id="divide", variant="warning"),
|
||||
Button("7", id="number-7"),
|
||||
Button("8", id="number-8"),
|
||||
Button("9", id="number-9"),
|
||||
Button("×", id="multiply", variant="warning"),
|
||||
Button("4", id="number-4"),
|
||||
Button("5", id="number-5"),
|
||||
Button("6", id="number-6"),
|
||||
Button("-", id="minus", variant="warning"),
|
||||
Button("1", id="number-1"),
|
||||
Button("2", id="number-2"),
|
||||
Button("3", id="number-3"),
|
||||
Button("+", id="plus", variant="warning"),
|
||||
Button("0", id="number-0"),
|
||||
Button(".", id="point"),
|
||||
Button("=", id="equals", variant="warning"),
|
||||
id="calculator",
|
||||
)
|
||||
|
||||
def on_key(self, event: events.Key) -> None:
|
||||
"""Called when the user presses a key."""
|
||||
|
||||
print(f"KEY {event} was pressed!")
|
||||
|
||||
def press(button_id: str) -> None:
|
||||
self.query_one(f"#{button_id}", Button).press()
|
||||
self.set_focus(None)
|
||||
|
||||
key = event.key
|
||||
if key.isdecimal():
|
||||
press(f"number-{key}")
|
||||
elif key == "c":
|
||||
press("c")
|
||||
press("ac")
|
||||
elif key in self.KEY_MAP:
|
||||
press(self.KEY_MAP[key])
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
"""Called when a button is pressed."""
|
||||
|
||||
button_id = event.button.id
|
||||
assert button_id is not None
|
||||
|
||||
self.bell() # Terminal bell
|
||||
|
||||
def do_math() -> None:
|
||||
"""Does the math: LEFT OPERATOR RIGHT"""
|
||||
try:
|
||||
if self.operator == "plus":
|
||||
self.left += self.right
|
||||
elif self.operator == "minus":
|
||||
self.left -= self.right
|
||||
elif self.operator == "divide":
|
||||
self.left /= self.right
|
||||
elif self.operator == "multiply":
|
||||
self.left *= self.right
|
||||
self.numbers = str(self.left)
|
||||
self.value = ""
|
||||
except Exception:
|
||||
self.numbers = "Error"
|
||||
|
||||
if button_id.startswith("number-"):
|
||||
number = button_id.partition("-")[-1]
|
||||
self.numbers = self.value = self.value.lstrip("0") + number
|
||||
elif button_id == "plus-minus":
|
||||
self.numbers = self.value = str(Decimal(self.value or "0") * -1)
|
||||
elif button_id == "percent":
|
||||
self.numbers = self.value = str(Decimal(self.value or "0") / Decimal(100))
|
||||
elif button_id == "point":
|
||||
if "." not in self.value:
|
||||
self.numbers = self.value = (self.value or "0") + "."
|
||||
elif button_id == "ac":
|
||||
self.value = ""
|
||||
self.left = self.right = Decimal(0)
|
||||
self.operator = "plus"
|
||||
self.numbers = "0"
|
||||
elif button_id == "c":
|
||||
self.value = ""
|
||||
self.numbers = "0"
|
||||
elif button_id in ("plus", "minus", "divide", "multiply"):
|
||||
self.right = Decimal(self.value or "0")
|
||||
do_math()
|
||||
self.operator = button_id
|
||||
elif button_id == "equals":
|
||||
if self.value:
|
||||
self.right = Decimal(self.value)
|
||||
do_math()
|
||||
|
||||
|
||||
app = CalculatorApp(css_path="calculator.css")
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -3,7 +3,7 @@ from textual.widgets import Static
|
||||
|
||||
|
||||
class CenterApp(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
|
||||
CenterApp Screen {
|
||||
layout: center;
|
||||
|
||||
@@ -4,7 +4,7 @@ from textual.widgets import Static
|
||||
|
||||
|
||||
class CenterApp(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
|
||||
#sidebar {
|
||||
dock: left;
|
||||
@@ -52,4 +52,4 @@ class CenterApp(App):
|
||||
)
|
||||
|
||||
|
||||
app = CenterApp(log_verbosity=3)
|
||||
app = CenterApp()
|
||||
|
||||
17
sandbox/will/input.py
Normal file
17
sandbox/will/input.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from textual.app import App
|
||||
from textual.widgets import TextInput
|
||||
|
||||
|
||||
class InputApp(App):
|
||||
|
||||
CSS = """
|
||||
TextInput {
|
||||
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self):
|
||||
yield TextInput(initial="foo")
|
||||
|
||||
|
||||
app = InputApp()
|
||||
@@ -10,7 +10,7 @@ from textual.widget import Widget
|
||||
|
||||
|
||||
class Box(Widget, can_focus=True):
|
||||
CSS = "#box {background: blue;}"
|
||||
DEFAULT_CSS = "#box {background: blue;}"
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return Panel("Box")
|
||||
|
||||
@@ -21,7 +21,7 @@ class NewScreen(Screen):
|
||||
|
||||
|
||||
class ScreenApp(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
ScreenApp Screen {
|
||||
background: #111144;
|
||||
color: white;
|
||||
|
||||
@@ -37,9 +37,7 @@ class ButtonsApp(App[str]):
|
||||
self.dark = not self.dark
|
||||
|
||||
|
||||
app = ButtonsApp(
|
||||
log_path="textual.log", css_path="buttons.css", watch_css=True, log_verbosity=3
|
||||
)
|
||||
app = ButtonsApp(log_path="textual.log", css_path="buttons.css", watch_css=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = app.run()
|
||||
|
||||
@@ -8,5 +8,6 @@ Static {
|
||||
background: blue 20%;
|
||||
height: 100%;
|
||||
margin: 2 4;
|
||||
min-width: 30;
|
||||
min-width: 80;
|
||||
min-height: 40;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,14 @@ from textual.app import App
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
class Clickable(Static):
|
||||
def on_click(self):
|
||||
self.app.bell()
|
||||
|
||||
|
||||
class SpacingApp(App):
|
||||
def compose(self):
|
||||
yield Static()
|
||||
yield Static(id="2332")
|
||||
|
||||
|
||||
app = SpacingApp(css_path="spacing.css")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Screen {
|
||||
layout: table;
|
||||
table-columns: 2fr 1fr 1fr;
|
||||
table-rows: 1fr 1fr;
|
||||
table-gutter: 1 2;
|
||||
layout: grid;
|
||||
grid-columns: 2fr 1fr 1fr;
|
||||
grid-rows: 1fr 1fr;
|
||||
grid-gutter: 1 2;
|
||||
}
|
||||
|
||||
Static {
|
||||
|
||||
@@ -5,7 +5,7 @@ from textual.widgets import DirectoryTree
|
||||
|
||||
|
||||
class TreeApp(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Screen {
|
||||
overflow: auto;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user