mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
fix lock
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
# Dev Sandbox
|
||||
|
||||
This directory contains test code for Textual devs to experiment with new features. None of the .py files here are guaranteed to run or do anything useful, but you are welcome to look around.
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
|
||||
Screen {
|
||||
layout: vertical;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
Widget {
|
||||
margin:1;
|
||||
}
|
||||
|
||||
#thing {
|
||||
width: auto;
|
||||
height: auto;
|
||||
background:magenta;
|
||||
margin: 1;
|
||||
padding: 1;
|
||||
border: solid white;
|
||||
box-sizing: border-box;
|
||||
border: solid white;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
|
||||
#thing2 {
|
||||
border: solid white;
|
||||
/* outline: heavy blue; */
|
||||
height: 10;
|
||||
padding: 1 2;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
max-height: 100vh;
|
||||
|
||||
background:green;
|
||||
align-horizontal: center;
|
||||
color:white;
|
||||
}
|
||||
|
||||
|
||||
#thing3 {
|
||||
height: 10;
|
||||
margin: 1;
|
||||
background:blue;
|
||||
color: white 50%;
|
||||
border: white;
|
||||
align-horizontal: center;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
from rich.style import Style
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
class Thing(Widget):
|
||||
def render(self):
|
||||
return "Hello, 3434 World.\n[b]Lorem impsum."
|
||||
|
||||
|
||||
class AlignApp(App):
|
||||
CSS_PATH = "align.css"
|
||||
|
||||
def on_load(self):
|
||||
self.bind("t", "log_tree")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Thing(id="thing")
|
||||
yield Static("foo", id="thing2")
|
||||
yield Widget(id="thing3")
|
||||
|
||||
def action_log_tree(self):
|
||||
self.log(self.screen.tree)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = AlignApp(css_path="align.css")
|
||||
app.run()
|
||||
@@ -1,16 +0,0 @@
|
||||
Vertical {
|
||||
background: red 50%;
|
||||
}
|
||||
|
||||
.test {
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
background: white 50%;
|
||||
border:solid green;
|
||||
padding: 0;
|
||||
margin:3;
|
||||
|
||||
align: center middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Static
|
||||
from textual.layout import Vertical
|
||||
|
||||
from rich.text import Text
|
||||
|
||||
TEXT = Text.from_markup(" ".join(str(n) * 5 for n in range(12)))
|
||||
|
||||
|
||||
class AutoApp(App):
|
||||
CSS_PATH = "auto_test.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Vertical(
|
||||
Static(TEXT, classes="test"), Static(TEXT, id="test", classes="test")
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = AutoApp()
|
||||
app.run()
|
||||
@@ -1,66 +0,0 @@
|
||||
from rich.console import RenderableType
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.css.types import EdgeType
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Placeholder
|
||||
|
||||
|
||||
class VerticalContainer(Widget):
|
||||
DEFAULT_CSS = """
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
overflow: hidden auto;
|
||||
background: darkblue;
|
||||
}
|
||||
|
||||
VerticalContainer Placeholder {
|
||||
margin: 1 0;
|
||||
height: auto;
|
||||
align: center top;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class Introduction(Widget):
|
||||
DEFAULT_CSS = """
|
||||
Introduction {
|
||||
background: indigo;
|
||||
color: white;
|
||||
height: 3;
|
||||
padding: 1 0;
|
||||
}
|
||||
"""
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return Text("Here are the color edge types we support.", justify="center")
|
||||
|
||||
|
||||
class BorderDemo(Widget):
|
||||
def __init__(self, name: str):
|
||||
super().__init__(name=name)
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return Text(self.name, style="black on yellow", justify="center")
|
||||
|
||||
|
||||
class MyTestApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
border_demo_widgets = []
|
||||
for border_edge_type in EdgeType.__args__:
|
||||
border_demo = BorderDemo(f'"border: {border_edge_type} white"')
|
||||
border_demo.styles.height = "auto"
|
||||
border_demo.styles.margin = (1, 0)
|
||||
border_demo.styles.border = (border_edge_type, "white")
|
||||
border_demo_widgets.append(border_demo)
|
||||
|
||||
yield VerticalContainer(Introduction(), *border_demo_widgets, id="root")
|
||||
|
||||
def on_mount(self):
|
||||
self.bind("q", "quit")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = MyTestApp()
|
||||
app.run()
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
Button {
|
||||
box-sizing: border-box;
|
||||
margin: 1;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
from textual import layout, events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Button
|
||||
|
||||
|
||||
class ButtonsApp(App[str]):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield layout.Vertical(
|
||||
Button("default", id="foo"),
|
||||
Button.success("success", id="bar"),
|
||||
Button.warning("warning", id="baz"),
|
||||
Button.error("error", id="baz"),
|
||||
)
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
self.app.bell()
|
||||
|
||||
async def on_key(self, event: events.Key) -> None:
|
||||
await self.dispatch_key(event)
|
||||
|
||||
def key_d(self):
|
||||
self.dark = not self.dark
|
||||
|
||||
|
||||
app = ButtonsApp(
|
||||
log_path="textual.log",
|
||||
css_path="buttons.css",
|
||||
watch_css=True,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = app.run()
|
||||
print(repr(result))
|
||||
@@ -1,46 +0,0 @@
|
||||
import rich.repr
|
||||
from rich.align import Align
|
||||
from rich.console import RenderableType
|
||||
from rich.panel import Panel
|
||||
from rich.pretty import Pretty
|
||||
|
||||
from textual._color_constants import COLOR_NAME_TO_RGB
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Placeholder
|
||||
|
||||
|
||||
@rich.repr.auto(angular=False)
|
||||
class ColorDisplay(Widget, can_focus=True):
|
||||
def render(self) -> RenderableType:
|
||||
return Panel(
|
||||
Align.center(
|
||||
Pretty(self, no_wrap=True, overflow="ellipsis"), vertical="middle"
|
||||
),
|
||||
title=self.name,
|
||||
border_style="none",
|
||||
)
|
||||
|
||||
|
||||
class ColorNames(App):
|
||||
DEFAULT_CSS = """
|
||||
ColorDisplay {
|
||||
height: 1;
|
||||
}
|
||||
"""
|
||||
|
||||
def on_mount(self):
|
||||
self.bind("q", "quit")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
for color_name, color in COLOR_NAME_TO_RGB.items():
|
||||
color_placeholder = ColorDisplay(name=color_name)
|
||||
is_dark_color = sum(color) < 400
|
||||
color_placeholder.styles.color = "white" if is_dark_color else "black"
|
||||
color_placeholder.styles.background = color_name
|
||||
yield color_placeholder
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
color_name_app = ColorNames()
|
||||
color_name_app.run()
|
||||
@@ -1,54 +0,0 @@
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
class FocusKeybindsApp(App):
|
||||
dark = True
|
||||
|
||||
def on_load(self) -> None:
|
||||
self.bind("1", "focus('widget1')")
|
||||
self.bind("2", "focus('widget2')")
|
||||
self.bind("3", "focus('widget3')")
|
||||
self.bind("4", "focus('widget4')")
|
||||
self.bind("q", "focus('widgetq')")
|
||||
self.bind("w", "focus('widgetw')")
|
||||
self.bind("e", "focus('widgete')")
|
||||
self.bind("r", "focus('widgetr')")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
info = Static(
|
||||
"Use keybinds to shift focus between the widgets in the lists below",
|
||||
)
|
||||
self.mount(info=info)
|
||||
|
||||
self.mount(
|
||||
body=Widget(
|
||||
Widget(
|
||||
Static("Press 1 to focus", id="widget1", classes="list-item"),
|
||||
Static("Press 2 to focus", id="widget2", classes="list-item"),
|
||||
Static("Press 3 to focus", id="widget3", classes="list-item"),
|
||||
Static("Press 4 to focus", id="widget4", classes="list-item"),
|
||||
classes="list",
|
||||
id="left_list",
|
||||
),
|
||||
Widget(
|
||||
Static("Press Q to focus", id="widgetq", classes="list-item"),
|
||||
Static("Press W to focus", id="widgetw", classes="list-item"),
|
||||
Static("Press E to focus", id="widgete", classes="list-item"),
|
||||
Static("Press R to focus", id="widgetr", classes="list-item"),
|
||||
classes="list",
|
||||
id="right_list",
|
||||
),
|
||||
),
|
||||
)
|
||||
self.mount(footer=Static("No widget focused"))
|
||||
|
||||
def on_descendant_focus(self):
|
||||
self.get_child("footer").update(
|
||||
f"Focused: {self.focused.id}" or "No widget focused"
|
||||
)
|
||||
|
||||
|
||||
app = FocusKeybindsApp(css_path="focus_keybindings.scss", watch_css=True)
|
||||
app.run()
|
||||
@@ -1,57 +0,0 @@
|
||||
App > Screen {
|
||||
layout: dock;
|
||||
docks: left=left top=top;
|
||||
}
|
||||
|
||||
#info {
|
||||
background: $primary;
|
||||
dock: top;
|
||||
height: 3;
|
||||
padding: 1;
|
||||
}
|
||||
|
||||
#body {
|
||||
dock: top;
|
||||
layout: dock;
|
||||
docks: bodylhs=left;
|
||||
}
|
||||
|
||||
#left_list {
|
||||
dock: bodylhs;
|
||||
padding: 2;
|
||||
}
|
||||
|
||||
#right_list {
|
||||
dock: bodylhs;
|
||||
padding: 2;
|
||||
}
|
||||
|
||||
#footer {
|
||||
height: 1;
|
||||
background: $secondary;
|
||||
padding: 0 1;
|
||||
dock: top;
|
||||
}
|
||||
|
||||
.list {
|
||||
background: $surface;
|
||||
border-top: hkey $surface-darken-1;
|
||||
}
|
||||
|
||||
.list:focus-within {
|
||||
background: $primary-darken-1;
|
||||
outline-top: $accent-lighten-1;
|
||||
outline-bottom: $accent-lighten-1;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
background: $surface;
|
||||
height: auto;
|
||||
border: $surface-darken-1 tall;
|
||||
padding: 0 1;
|
||||
}
|
||||
|
||||
.list-item:focus {
|
||||
background: $surface-darken-1;
|
||||
outline: $accent tall;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
from rich.console import RenderableType
|
||||
from rich.panel import Panel
|
||||
from rich.style import Style
|
||||
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class PanelWidget(Widget):
|
||||
def render(self) -> RenderableType:
|
||||
return Panel("hello world!", title="Title")
|
||||
|
||||
|
||||
class BasicApp(App):
|
||||
"""Sandbox application used for testing/development by Textual developers"""
|
||||
|
||||
def on_load(self):
|
||||
"""Bind keys here."""
|
||||
self.bind("tab", "toggle_class('#sidebar', '-active')")
|
||||
self.bind("a", "toggle_class('#header', '-visible')")
|
||||
self.bind("c", "toggle_class('#content', '-content-visible')")
|
||||
self.bind("d", "toggle_class('#footer', 'dim')")
|
||||
self.bind("x", "dump")
|
||||
|
||||
def on_mount(self):
|
||||
"""Build layout here."""
|
||||
self.mount(
|
||||
header=Widget(),
|
||||
content=PanelWidget(),
|
||||
footer=Widget(),
|
||||
sidebar=Widget(),
|
||||
)
|
||||
|
||||
def action_dump(self):
|
||||
self.panic(self.tree)
|
||||
|
||||
|
||||
BasicApp.run(css_path="dev_sandbox.scss", watch_css=True, log_path="textual.log")
|
||||
@@ -1,66 +0,0 @@
|
||||
/* CSS file for dev_sandbox.py */
|
||||
|
||||
$text: #f0f0f0;
|
||||
$primary: #021720;
|
||||
$secondary: #95d52a;
|
||||
$background: #262626;
|
||||
|
||||
$animatitext-speed: 500ms;
|
||||
$animation: offset $animatitext-speed in_out_cubic;
|
||||
|
||||
App > View {
|
||||
docks: side=left/1;
|
||||
background: $background;
|
||||
}
|
||||
|
||||
Widget:hover {
|
||||
outline: heavy;
|
||||
text-style: bold !important;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
color: $text;
|
||||
background: $background;
|
||||
dock: side;
|
||||
width: 30;
|
||||
offset-x: -100%;
|
||||
transition: $animation;
|
||||
border-right: outer $secondary;
|
||||
}
|
||||
|
||||
#sidebar.-active {
|
||||
offset-x: 0;
|
||||
}
|
||||
|
||||
#header {
|
||||
color: $text;
|
||||
background: $primary;
|
||||
height: 3;
|
||||
border-bottom: hkey $secondary;
|
||||
}
|
||||
|
||||
#header.-visible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#content {
|
||||
color: $text;
|
||||
background: $background;
|
||||
offset-y: -3;
|
||||
}
|
||||
|
||||
#content.-content-visible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#footer {
|
||||
text-opacity: 1;
|
||||
color: $text;
|
||||
background: $background;
|
||||
height: 3;
|
||||
border-top: hkey $secondary;
|
||||
}
|
||||
|
||||
#footer.dim {
|
||||
text-opacity: 0.5;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
from textual.app import App
|
||||
from textual import layout
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class FiftyApp(App):
|
||||
|
||||
DEFAULT_CSS = """
|
||||
Screen {
|
||||
layout: vertical;
|
||||
}
|
||||
Horizontal {
|
||||
height: 50%;
|
||||
}
|
||||
Widget {
|
||||
width: 50%;
|
||||
outline: white;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
def compose(self):
|
||||
yield layout.Horizontal(Widget(), Widget())
|
||||
yield layout.Horizontal(Widget(), Widget())
|
||||
|
||||
|
||||
app = FiftyApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,18 +0,0 @@
|
||||
Horizontal {
|
||||
background: red 50%;
|
||||
overflow-x: auto;
|
||||
/* width: auto */
|
||||
}
|
||||
|
||||
.test {
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
background: white 50%;
|
||||
border:solid green;
|
||||
padding: 0;
|
||||
margin:3;
|
||||
|
||||
align: center middle;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Static
|
||||
from textual import layout
|
||||
|
||||
from rich.text import Text
|
||||
|
||||
TEXT = Text.from_markup(" ".join(str(n) * 5 for n in range(10)))
|
||||
|
||||
|
||||
class AutoApp(App):
|
||||
def on_mount(self) -> None:
|
||||
self.bind("t", "tree")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield layout.Horizontal(
|
||||
Static(TEXT, classes="test"), Static(TEXT, id="test", classes="test")
|
||||
)
|
||||
|
||||
def action_tree(self):
|
||||
self.log(self.screen.tree)
|
||||
|
||||
|
||||
app = AutoApp(css_path="horizontal.css")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,67 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
|
||||
from textual.widgets.text_input import TextInput, TextWidgetBase, TextArea
|
||||
|
||||
|
||||
def celsius_to_fahrenheit(celsius: float) -> float:
|
||||
return celsius * 1.8 + 32
|
||||
|
||||
|
||||
def fahrenheit_to_celsius(fahrenheit: float) -> float:
|
||||
return (fahrenheit - 32) / 1.8
|
||||
|
||||
|
||||
words = set(Path("/usr/share/dict/words").read_text().splitlines())
|
||||
|
||||
|
||||
def word_autocompleter(value: str) -> str | None:
|
||||
# An example autocompleter that uses the Unix dictionary to suggest
|
||||
# word completions
|
||||
for word in words:
|
||||
if word.startswith(value):
|
||||
return word
|
||||
return None
|
||||
|
||||
|
||||
class InputApp(App[str]):
|
||||
def on_mount(self) -> None:
|
||||
self.fahrenheit = TextInput(placeholder="Fahrenheit", id="fahrenheit")
|
||||
self.celsius = TextInput(placeholder="Celsius", id="celsius")
|
||||
self.fahrenheit.focus()
|
||||
text_boxes = Widget(self.fahrenheit, self.celsius)
|
||||
self.mount(inputs=text_boxes)
|
||||
self.mount(spacer=Widget())
|
||||
self.mount(
|
||||
top_search=Widget(
|
||||
TextInput(autocompleter=word_autocompleter, id="topsearchbox")
|
||||
)
|
||||
)
|
||||
self.mount(
|
||||
footer=TextInput(
|
||||
placeholder="Footer Search Bar", autocompleter=word_autocompleter
|
||||
)
|
||||
)
|
||||
self.mount(text_area=TextArea())
|
||||
|
||||
def on_text_input_changed_changed(self, event: TextInput.Changed) -> None:
|
||||
try:
|
||||
value = float(event.value)
|
||||
except ValueError:
|
||||
return
|
||||
if event.sender == self.celsius:
|
||||
fahrenheit = celsius_to_fahrenheit(value)
|
||||
self.fahrenheit.value = f"{fahrenheit:.1f}"
|
||||
elif event.sender == self.fahrenheit:
|
||||
celsius = fahrenheit_to_celsius(value)
|
||||
self.celsius.value = f"{celsius:.1f}"
|
||||
|
||||
|
||||
app = InputApp(log_path="textual.log", css_path="input.scss", watch_css=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = app.run()
|
||||
@@ -1,53 +0,0 @@
|
||||
App {
|
||||
background: $secondary;
|
||||
}
|
||||
|
||||
#spacer {
|
||||
height: 1;
|
||||
background: $primary-darken-2;
|
||||
dock: top;
|
||||
}
|
||||
|
||||
Screen {
|
||||
layout: dock;
|
||||
docks: top=top bottom=bottom;
|
||||
background: $background;
|
||||
}
|
||||
|
||||
#fahrenheit {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#celsius {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#celsius :focus {
|
||||
border: heavy darkgoldenrod;
|
||||
}
|
||||
|
||||
#inputs {
|
||||
dock: top;
|
||||
background: $primary;
|
||||
height: 3;
|
||||
layout: horizontal;
|
||||
}
|
||||
|
||||
#text_area {
|
||||
dock: bottom;
|
||||
}
|
||||
|
||||
#top_search {
|
||||
dock: top;
|
||||
}
|
||||
|
||||
#topsearchbox {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
#footer {
|
||||
background: $primary-darken-2;
|
||||
dock: bottom;
|
||||
height: 1;
|
||||
border: ;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
App > View {
|
||||
layout: dock;
|
||||
}
|
||||
|
||||
Widget {
|
||||
text: on blue;
|
||||
}
|
||||
|
||||
Widget.-highlight {
|
||||
outline: heavy red;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
from textual import events
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Placeholder
|
||||
|
||||
|
||||
class BasicApp(App):
|
||||
"""Sandbox application used for testing/development by Textual developers"""
|
||||
|
||||
def on_mount(self):
|
||||
"""Build layout here."""
|
||||
self.mount(
|
||||
header=Widget(),
|
||||
content=Placeholder(),
|
||||
footer=Widget(),
|
||||
sidebar=Widget(),
|
||||
)
|
||||
|
||||
async def on_key(self, event: events.Key) -> None:
|
||||
await self.dispatch_key(event)
|
||||
|
||||
def key_a(self) -> None:
|
||||
footer = self.get_child("footer")
|
||||
footer.set_styles(text="on magenta")
|
||||
|
||||
def key_b(self) -> None:
|
||||
footer = self.get_child("footer")
|
||||
footer.set_styles("text: on green")
|
||||
|
||||
def key_c(self) -> None:
|
||||
header = self.get_child("header")
|
||||
header.toggle_class("-highlight")
|
||||
self.log(header.styles)
|
||||
|
||||
|
||||
BasicApp.run(css_path="local_styles.css", log_path="textual.log")
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
|
||||
Vertical {
|
||||
background: blue;
|
||||
|
||||
}
|
||||
|
||||
#container {
|
||||
width:50%;
|
||||
height: auto;
|
||||
align-horizontal: center;
|
||||
padding: 1;
|
||||
border: heavy white;
|
||||
background: white 50%;
|
||||
overflow-y: auto
|
||||
}
|
||||
|
||||
TextWidget {
|
||||
/* width: 50%; */
|
||||
height: auto;
|
||||
padding: 2;
|
||||
background: green 30%;
|
||||
border: yellow;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widget import Widget
|
||||
from textual import layout
|
||||
|
||||
|
||||
from rich.text import Text
|
||||
|
||||
|
||||
lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||
|
||||
TEXT = Text.from_markup(lorem)
|
||||
|
||||
|
||||
class TextWidget(Widget):
|
||||
def render(self):
|
||||
return TEXT
|
||||
|
||||
|
||||
class AutoApp(App, css_path="nest.css"):
|
||||
def on_mount(self) -> None:
|
||||
self.bind("t", "tree")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield layout.Vertical(
|
||||
Widget(
|
||||
TextWidget(classes="test"),
|
||||
id="container",
|
||||
),
|
||||
)
|
||||
|
||||
def action_tree(self):
|
||||
self.log(self.screen.tree)
|
||||
@@ -1,72 +0,0 @@
|
||||
from rich.console import RenderableType
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Placeholder
|
||||
|
||||
placeholders_count = 12
|
||||
|
||||
|
||||
class VerticalContainer(Widget):
|
||||
DEFAULT_CSS = """
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
overflow: hidden auto;
|
||||
background: darkblue;
|
||||
}
|
||||
|
||||
VerticalContainer Placeholder {
|
||||
margin: 1 0;
|
||||
height: 5;
|
||||
border: solid lime;
|
||||
align: center top;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class Introduction(Widget):
|
||||
DEFAULT_CSS = """
|
||||
Introduction {
|
||||
background: indigo;
|
||||
color: white;
|
||||
height: 3;
|
||||
padding: 1 0;
|
||||
}
|
||||
"""
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return Text(
|
||||
"Press keys 0 to 9 to scroll to the Placeholder with that ID.",
|
||||
justify="center",
|
||||
)
|
||||
|
||||
|
||||
class MyTestApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
placeholders = [
|
||||
Placeholder(id=f"placeholder_{i}", name=f"Placeholder #{i}")
|
||||
for i in range(placeholders_count)
|
||||
]
|
||||
|
||||
yield VerticalContainer(Introduction(), *placeholders, id="root")
|
||||
|
||||
def on_mount(self):
|
||||
self.bind("q", "quit")
|
||||
self.bind("t", "tree")
|
||||
for widget_index in range(placeholders_count):
|
||||
self.bind(str(widget_index), f"scroll_to('placeholder_{widget_index}')")
|
||||
|
||||
def action_tree(self):
|
||||
self.log(self.tree)
|
||||
|
||||
async def action_scroll_to(self, target_placeholder_id: str):
|
||||
target_placeholder = self.query(f"#{target_placeholder_id}").first()
|
||||
target_placeholder_container = self.query("#root").first()
|
||||
target_placeholder_container.scroll_to_widget(target_placeholder, animate=True)
|
||||
|
||||
|
||||
app = MyTestApp()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,3 +0,0 @@
|
||||
from textual.app import App
|
||||
|
||||
app = App()
|
||||
@@ -1,28 +0,0 @@
|
||||
App.-show-focus *:focus {
|
||||
tint: #8bc34a 20%;
|
||||
}
|
||||
|
||||
#uber1 {
|
||||
layout: vertical;
|
||||
background: green;
|
||||
overflow: hidden auto;
|
||||
border: heavy white;
|
||||
text-style: underline;
|
||||
/* box-sizing: content-box; */
|
||||
}
|
||||
|
||||
#uber1:focus-within {
|
||||
background: darkslateblue;
|
||||
}
|
||||
|
||||
#child2 {
|
||||
text-style: underline;
|
||||
background: red 10%;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
height: 10;
|
||||
/* display: none; */
|
||||
color: #12a0;
|
||||
background: #ffffff00;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import random
|
||||
import sys
|
||||
|
||||
from textual import events
|
||||
from textual.app import App
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Placeholder
|
||||
|
||||
|
||||
class BasicApp(App):
|
||||
"""Sandbox application used for testing/development by Textual developers"""
|
||||
|
||||
def on_load(self):
|
||||
self.bind("q", "quit", "Quit")
|
||||
self.bind("d", "dump")
|
||||
self.bind("t", "log_tree")
|
||||
self.bind("p", "print")
|
||||
self.bind("v", "toggle_visibility")
|
||||
self.bind("x", "toggle_display")
|
||||
self.bind("f", "modify_focussed")
|
||||
self.bind("b", "toggle_border")
|
||||
|
||||
async def on_mount(self):
|
||||
"""Build layout here."""
|
||||
first_child = Placeholder(id="child1", classes="list-item")
|
||||
uber1 = Widget(
|
||||
first_child,
|
||||
Placeholder(id="child2", classes="list-item"),
|
||||
Placeholder(id="child3", classes="list-item"),
|
||||
Placeholder(classes="list-item"),
|
||||
Placeholder(classes="list-item"),
|
||||
Placeholder(classes="list-item"),
|
||||
)
|
||||
self.mount(uber1=uber1)
|
||||
uber1.focus()
|
||||
self.first_child = first_child
|
||||
self.uber = uber1
|
||||
|
||||
async def on_key(self, event: events.Key) -> None:
|
||||
await self.dispatch_key(event)
|
||||
|
||||
def action_quit(self):
|
||||
self.panic(self.app.tree)
|
||||
|
||||
def action_dump(self):
|
||||
self.panic(str(self.app._registry))
|
||||
|
||||
def action_log_tree(self):
|
||||
self.log(self.screen.tree)
|
||||
|
||||
def action_print(self):
|
||||
print(
|
||||
"Focused widget is:",
|
||||
self.focused,
|
||||
)
|
||||
self.app.set_focus(None)
|
||||
|
||||
def action_modify_focussed(self):
|
||||
"""Increment height of focussed child, randomise border and bg color"""
|
||||
previous_height = self.focused.styles.height.value
|
||||
new_height = previous_height + 1
|
||||
self.focused.styles.height = self.focused.styles.height.copy_with(
|
||||
value=new_height
|
||||
)
|
||||
color = random.choice(["red", "green", "blue"])
|
||||
self.focused.styles.background = color
|
||||
self.focused.styles.border = ("dashed", color)
|
||||
|
||||
def action_toggle_visibility(self):
|
||||
self.focused.visible = not self.focused.visible
|
||||
|
||||
def action_toggle_display(self):
|
||||
# TODO: Doesn't work
|
||||
self.focused.display = not self.focused.display
|
||||
|
||||
def action_toggle_border(self):
|
||||
self.focused.styles.border_top = ("solid", "invalid-color")
|
||||
|
||||
|
||||
app = BasicApp(css_path="uber.css")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,22 +0,0 @@
|
||||
Screen {
|
||||
background:blue;
|
||||
}
|
||||
|
||||
Vertical {
|
||||
background: red 50%;
|
||||
overflow: auto;
|
||||
/* width: auto */
|
||||
}
|
||||
|
||||
.test {
|
||||
/* width: auto; */
|
||||
/* height: 50vh; */
|
||||
|
||||
background: white 50%;
|
||||
border:solid green;
|
||||
padding: 0;
|
||||
margin:3;
|
||||
|
||||
align: center middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Static
|
||||
from textual import layout
|
||||
|
||||
from rich.text import Text
|
||||
|
||||
TEXT = Text.from_markup(" ".join(str(n) * 5 for n in range(10)))
|
||||
|
||||
|
||||
class AutoApp(App):
|
||||
def on_mount(self) -> None:
|
||||
self.bind("t", "tree")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield layout.Horizontal(
|
||||
layout.Vertical(
|
||||
Static(TEXT, classes="test"),
|
||||
Static(TEXT, id="test", classes="test"),
|
||||
)
|
||||
)
|
||||
|
||||
def action_tree(self):
|
||||
self.log(self.screen.tree)
|
||||
|
||||
|
||||
app = AutoApp(css_path="vertical.css")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,94 +0,0 @@
|
||||
from rich.console import RenderableType
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Placeholder
|
||||
|
||||
root_container_style = "border: solid white;"
|
||||
initial_placeholders_count = 4
|
||||
|
||||
|
||||
class VerticalContainer(Widget):
|
||||
DEFAULT_CSS = """
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
overflow: hidden auto;
|
||||
background: darkblue;
|
||||
${root_container_style}
|
||||
}
|
||||
|
||||
VerticalContainer Placeholder {
|
||||
margin: 1 0;
|
||||
height: 5;
|
||||
border: solid lime;
|
||||
align: center top;
|
||||
}
|
||||
""".replace(
|
||||
"${root_container_style}", root_container_style
|
||||
)
|
||||
|
||||
|
||||
class Introduction(Widget):
|
||||
DEFAULT_CSS = """
|
||||
Introduction {
|
||||
background: indigo;
|
||||
color: white;
|
||||
height: 3;
|
||||
padding: 1 0;
|
||||
}
|
||||
"""
|
||||
|
||||
def render(self, styles) -> RenderableType:
|
||||
return Text(
|
||||
"Press '-' and '+' to add or remove placeholders.", justify="center"
|
||||
)
|
||||
|
||||
|
||||
class MyTestApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
# yield Introduction()
|
||||
|
||||
placeholders = [
|
||||
Placeholder(id=f"placeholder_{i}", name=f"Placeholder #{i}")
|
||||
for i in range(initial_placeholders_count)
|
||||
]
|
||||
|
||||
yield VerticalContainer(Introduction(), *placeholders, id="root")
|
||||
|
||||
def on_mount(self):
|
||||
self.bind("q", "quit")
|
||||
self.bind("t", "tree")
|
||||
self.bind("-", "remove_placeholder")
|
||||
self.bind("+", "add_placeholder")
|
||||
|
||||
def action_tree(self):
|
||||
self.log(self.tree)
|
||||
|
||||
async def action_remove_placeholder(self):
|
||||
placeholders = self.query("Placeholder")
|
||||
placeholders_count = len(placeholders)
|
||||
for i, placeholder in enumerate(placeholders):
|
||||
if i == placeholders_count - 1:
|
||||
await self.remove(placeholder)
|
||||
placeholder.parent.children._nodes.remove(placeholder)
|
||||
self.refresh(repaint=True, layout=True)
|
||||
self.refresh_css()
|
||||
|
||||
async def action_add_placeholder(self):
|
||||
placeholders = self.query("Placeholder")
|
||||
placeholders_count = len(placeholders)
|
||||
placeholder = Placeholder(
|
||||
id=f"placeholder_{placeholders_count}",
|
||||
name=f"Placeholder #{placeholders_count}",
|
||||
)
|
||||
root = self.get_child("root")
|
||||
root.mount(placeholder)
|
||||
self.refresh(repaint=True, layout=True)
|
||||
self.refresh_css()
|
||||
|
||||
|
||||
app = MyTestApp()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
Reference in New Issue
Block a user