screenshot

This commit is contained in:
Will McGugan
2022-10-20 14:02:55 +01:00
9 changed files with 351 additions and 29 deletions

View File

@@ -0,0 +1,8 @@
* {
transition: color 300ms linear, background 300ms linear;
}
#another-box {
background: $boost;
padding: 1 2;
}

View File

@@ -0,0 +1,28 @@
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.color import Color
from textual.widgets import Static
START_COLOR = Color.parse("#FF0000EE")
END_COLOR = Color.parse("#0000FF0F")
class ColorAnimate(App):
BINDINGS = [Binding("d", action="toggle_dark", description="Dark mode")]
def compose(self) -> ComposeResult:
self.box = Static("Hello, world", id="box")
self.box.styles.background = START_COLOR
self.another_box = Static("Another box with $boost", id="another-box")
yield self.box
yield self.another_box
def key_a(self):
self.animator.animate(self.box.styles, "background", END_COLOR, duration=2.0)
app = ColorAnimate(css_path="color_animate.css")
if __name__ == "__main__":
app.run()

View File

@@ -2,19 +2,21 @@ from __future__ import annotations
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.screen import Screen
from textual.widgets import Static, Footer, Header
class JustABox(App):
class MainScreen(Screen):
BINDINGS = [
Binding(
key="ctrl+t", action="text_fade_out", description="text-opacity fade out"
),
Binding(
key="o,f,w",
action="widget_fade_out",
description="opacity fade out",
key_display="o or f or w",
(
"o,f,w",
"widget_fade_out",
"opacity fade out",
# key_display="o or f or w",
),
]
@@ -25,11 +27,16 @@ class JustABox(App):
def action_text_fade_out(self) -> None:
box = self.query_one("#box1")
self.animator.animate(box.styles, "text_opacity", value=0.0, duration=1)
self.app.animator.animate(box.styles, "text_opacity", value=0.0, duration=1)
def action_widget_fade_out(self) -> None:
box = self.query_one("#box1")
self.animator.animate(box.styles, "opacity", value=0.0, duration=1)
self.app.animator.animate(box.styles, "opacity", value=0.0, duration=1)
class JustABox(App):
def on_mount(self):
self.push_screen(MainScreen())
def key_d(self):
print(self.screen.styles.get_rules())

View File

@@ -480,7 +480,7 @@ class App(Generic[ReturnType], DOMNode):
"""Action to toggle dark mode."""
self.dark = not self.dark
def action_screenshot(self, filename: str | None, path: str = "~/") -> None:
def action_screenshot(self, filename: str | None = None, path: str = "./") -> None:
"""Save an SVG "screenshot". This action will save an SVG file containing the current contents of the screen.
Args:

View File

@@ -1,10 +1,14 @@
* {
transition: color 300ms linear, background 300ms linear;
}
ColorButtons {
dock: left;
overflow-y: auto;
width: 30;
}
ColorButtons > Button {
ColorButtons > Button {
width: 30;
}
@@ -18,7 +22,7 @@ ColorsView {
}
ColorItem {
layout: horizontal;
layout: horizontal;
height: 3;
width: 1fr;
}
@@ -36,15 +40,15 @@ ColorBar.label {
ColorItem {
width: 100%;
padding: 1 2;
padding: 1 2;
}
ColorGroup {
margin: 2 0;
width: 80;
width: 80;
height: auto;
padding: 1 4 2 4;
background: $surface;
background: $surface;
border: wide $surface;
}
@@ -73,3 +77,256 @@ ColorLabel {
color: $text;
text-style: bold;
}
.primary-darken-3 {
background: $primary-darken-3;
}
.primary-darken-2 {
background: $primary-darken-2;
}
.primary-darken-1 {
background: $primary-darken-1;
}
.primary {
background: $primary;
}
.primary-lighten-1 {
background: $primary-lighten-1;
}
.primary-lighten-2 {
background: $primary-lighten-2;
}
.primary-lighten-3 {
background: $primary-lighten-3;
}
.secondary-darken-3 {
background: $secondary-darken-3;
}
.secondary-darken-2 {
background: $secondary-darken-2;
}
.secondary-darken-1 {
background: $secondary-darken-1;
}
.secondary {
background: $secondary;
}
.secondary-lighten-1 {
background: $secondary-lighten-1;
}
.secondary-lighten-2 {
background: $secondary-lighten-2;
}
.secondary-lighten-3 {
background: $secondary-lighten-3;
}
.background-darken-3 {
background: $background-darken-3;
}
.background-darken-2 {
background: $background-darken-2;
}
.background-darken-1 {
background: $background-darken-1;
}
.background {
background: $background;
}
.background-lighten-1 {
background: $background-lighten-1;
}
.background-lighten-2 {
background: $background-lighten-2;
}
.background-lighten-3 {
background: $background-lighten-3;
}
.primary-background-darken-3 {
background: $primary-background-darken-3;
}
.primary-background-darken-2 {
background: $primary-background-darken-2;
}
.primary-background-darken-1 {
background: $primary-background-darken-1;
}
.primary-background {
background: $primary-background;
}
.primary-background-lighten-1 {
background: $primary-background-lighten-1;
}
.primary-background-lighten-2 {
background: $primary-background-lighten-2;
}
.primary-background-lighten-3 {
background: $primary-background-lighten-3;
}
.secondary-background-darken-3 {
background: $secondary-background-darken-3;
}
.secondary-background-darken-2 {
background: $secondary-background-darken-2;
}
.secondary-background-darken-1 {
background: $secondary-background-darken-1;
}
.secondary-background {
background: $secondary-background;
}
.secondary-background-lighten-1 {
background: $secondary-background-lighten-1;
}
.secondary-background-lighten-2 {
background: $secondary-background-lighten-2;
}
.secondary-background-lighten-3 {
background: $secondary-background-lighten-3;
}
.surface-darken-3 {
background: $surface-darken-3;
}
.surface-darken-2 {
background: $surface-darken-2;
}
.surface-darken-1 {
background: $surface-darken-1;
}
.surface {
background: $surface;
}
.surface-lighten-1 {
background: $surface-lighten-1;
}
.surface-lighten-2 {
background: $surface-lighten-2;
}
.surface-lighten-3 {
background: $surface-lighten-3;
}
.panel-darken-3 {
background: $panel-darken-3;
}
.panel-darken-2 {
background: $panel-darken-2;
}
.panel-darken-1 {
background: $panel-darken-1;
}
.panel {
background: $panel;
}
.panel-lighten-1 {
background: $panel-lighten-1;
}
.panel-lighten-2 {
background: $panel-lighten-2;
}
.panel-lighten-3 {
background: $panel-lighten-3;
}
.boost-darken-3 {
background: $boost-darken-3;
}
.boost-darken-2 {
background: $boost-darken-2;
}
.boost-darken-1 {
background: $boost-darken-1;
}
.boost {
background: $boost;
}
.boost-lighten-1 {
background: $boost-lighten-1;
}
.boost-lighten-2 {
background: $boost-lighten-2;
}
.boost-lighten-3 {
background: $boost-lighten-3;
}
.warning-darken-3 {
background: $warning-darken-3;
}
.warning-darken-2 {
background: $warning-darken-2;
}
.warning-darken-1 {
background: $warning-darken-1;
}
.warning {
background: $warning;
}
.warning-lighten-1 {
background: $warning-lighten-1;
}
.warning-lighten-2 {
background: $warning-lighten-2;
}
.warning-lighten-3 {
background: $warning-lighten-3;
}
.error-darken-3 {
background: $error-darken-3;
}
.error-darken-2 {
background: $error-darken-2;
}
.error-darken-1 {
background: $error-darken-1;
}
.error {
background: $error;
}
.error-lighten-1 {
background: $error-lighten-1;
}
.error-lighten-2 {
background: $error-lighten-2;
}
.error-lighten-3 {
background: $error-lighten-3;
}
.success-darken-3 {
background: $success-darken-3;
}
.success-darken-2 {
background: $success-darken-2;
}
.success-darken-1 {
background: $success-darken-1;
}
.success {
background: $success;
}
.success-lighten-1 {
background: $success-lighten-1;
}
.success-lighten-2 {
background: $success-lighten-2;
}
.success-lighten-3 {
background: $success-lighten-3;
}
.accent-darken-3 {
background: $accent-darken-3;
}
.accent-darken-2 {
background: $accent-darken-2;
}
.accent-darken-1 {
background: $accent-darken-1;
}
.accent {
background: $accent;
}
.accent-lighten-1 {
background: $accent-lighten-1;
}
.accent-lighten-2 {
background: $accent-lighten-2;
}
.accent-lighten-3 {
background: $accent-lighten-3;
}

View File

@@ -45,7 +45,6 @@ class ColorsView(Vertical):
"lighten-3",
]
variables = self.app.stylesheet._variables
for color_name in ColorSystem.COLOR_NAMES:
items: list[Widget] = [ColorLabel(f'"{color_name}"')]
@@ -55,8 +54,8 @@ class ColorsView(Vertical):
ColorBar(f"${color}", classes="text label"),
ColorBar(f"$text-muted", classes="muted"),
ColorBar(f"$text-disabled", classes="disabled"),
classes=color,
)
item.styles.background = variables[color]
items.append(item)
yield ColorGroup(*items, id=f"group-{color_name}")
@@ -84,12 +83,6 @@ class ColorsApp(App):
group.add_class("-active")
group.scroll_visible(speed=150)
def action_toggle_dark(self) -> None:
content = self.query_one("Content", Content)
self.dark = not self.dark
content.mount(ColorsView())
content.query("ColorsView").first().remove()
app = ColorsApp()

View File

@@ -349,18 +349,28 @@ class Color(NamedTuple):
Args:
destination (Color): Another color.
factor (float): A blend factor, 0 -> 1.
alpha (float | None): New alpha for result. Defaults to 1.
alpha (float | None): New alpha for result. Defaults to None.
Returns:
Color: A new color.
"""
if factor == 0:
return self
elif factor == 1:
return destination
r1, g1, b1, a1 = self
r2, g2, b2, a2 = destination
if alpha is None:
new_alpha = a1 + (a2 - a1) * factor
else:
new_alpha = alpha
return Color(
int(r1 + (r2 - r1) * factor),
int(g1 + (g2 - g1) * factor),
int(b1 + (b2 - b1) * factor),
a1 + (a2 - a1) * factor if alpha is None else alpha,
new_alpha,
)
def __add__(self, other: object) -> Color:

View File

@@ -1,3 +1,6 @@
* {
transition: background 250ms linear, color 250ms linear;
}
Screen {
layers: base overlay notes;
@@ -30,6 +33,7 @@ Sidebar Title {
OptionGroup {
background: $boost;
color: $text;
height: 1fr;
border-right: vkey $background;
}
@@ -179,7 +183,7 @@ LocationLink:hover {
}
DataTable {
height: 10;
height: 16;
}
LoginForm {
@@ -216,6 +220,7 @@ TreeControl {
Window {
background: $boost;
overflow: auto;
height: auto;
max-height: 16;

View File

@@ -93,6 +93,7 @@ Here's an example of some CSS used in this app:
"""
EXAMPLE_CSS = """\
Screen {
layers: base overlay notes;
@@ -286,8 +287,9 @@ class DemoApp(App):
CSS_PATH = "demo.css"
TITLE = "Textual Demo"
BINDINGS = [
("ctrl+s", "app.toggle_class('Sidebar', '-hidden')", "Sidebar"),
("ctrl+b", "app.toggle_class('Sidebar', '-hidden')", "Sidebar"),
("ctrl+t", "app.toggle_dark", "Toggle Dark mode"),
("ctrl+s", "app.screenshot()", "Screenshot"),
("f1", "app.toggle_class('TextLog', '-hidden')", "Notes"),
Binding("ctrl+c,ctrl+q", "app.quit", "Quit", show=True),
]
@@ -300,12 +302,12 @@ class DemoApp(App):
def compose(self) -> ComposeResult:
yield Container(
Sidebar(classes="-hidden"),
Header(),
Header(show_clock=True),
TextLog(classes="-hidden", wrap=False, highlight=True, markup=True),
Body(
QuickAccess(
LocationLink("TOP", ".location-top"),
LocationLink("Rich", ".location-rich"),
LocationLink("Rich content", ".location-rich"),
LocationLink("CSS", ".location-css"),
LocationLink("Widgets", ".location-widgets"),
),
@@ -319,7 +321,7 @@ class DemoApp(App):
SubTitle("Tables"),
Static(example_table, classes="table pad"),
SubTitle("JSON"),
Window(Static(JSON(JSON_EXAMPLE), expand=True, classes="pad")),
Window(Static(JSON(JSON_EXAMPLE), expand=True), classes="pad"),
),
classes="location-rich location-first",
),
@@ -366,6 +368,18 @@ class DemoApp(App):
table.zebra_stripes = True
for n in range(20):
table.add_row(*[f"Cell ([b]{n}[/b], {col})" for col in range(6)])
self.query_one("Welcome Button", Button).focus()
def action_screenshot(self, filename: str | None = None, path: str = "./") -> None:
"""Save an SVG "screenshot". This action will save an SVG file containing the current contents of the screen.
Args:
filename (str | None, optional): Filename of screenshot, or None to auto-generate. Defaults to None.
path (str, optional): Path to directory. Defaults to "~/".
"""
self.bell()
path = self.save_screenshot(filename, path)
self.add_note(f"Screenshot saved to {path!r}")
app = DemoApp()