mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Default css
This commit is contained in:
@@ -7,7 +7,7 @@ from textual.widget import Widget
|
||||
class Clock(Widget):
|
||||
"""A clock app."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Clock {
|
||||
content-align: center middle;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ from textual.widgets import Button
|
||||
|
||||
class ButtonApp(App):
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -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,7 @@ class ColorDisplay(Widget, can_focus=True):
|
||||
|
||||
|
||||
class ColorNames(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
ColorDisplay {
|
||||
height: 1;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -5,7 +5,7 @@ from textual.widgets import DirectoryTree
|
||||
|
||||
|
||||
class TreeApp(App):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Screen {
|
||||
overflow: auto;
|
||||
|
||||
|
||||
@@ -142,7 +142,11 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
"""
|
||||
|
||||
CSS = """
|
||||
# Inline CSS for quick scripts (generally css_path should be preferred.)
|
||||
CSS = ""
|
||||
|
||||
# Default (lowest priority) CSS
|
||||
DEFAULT_CSS = """
|
||||
App {
|
||||
background: $background;
|
||||
color: $text-background;
|
||||
@@ -683,7 +687,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
async def _on_css_change(self) -> None:
|
||||
"""Called when the CSS changes (if watch_css is True)."""
|
||||
if self.css_path is not None:
|
||||
|
||||
try:
|
||||
time = perf_counter()
|
||||
stylesheet = self.stylesheet.copy()
|
||||
@@ -1060,6 +1063,16 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.stylesheet.add_source(
|
||||
css, path=path, is_default_css=True, tie_breaker=tie_breaker
|
||||
)
|
||||
if self.CSS:
|
||||
try:
|
||||
app_css_path = (
|
||||
f"{inspect.getfile(self.__class__)}:{self.__class__.__name__}"
|
||||
)
|
||||
except TypeError:
|
||||
app_css_path = f"{self.__class__.__name__}"
|
||||
self.stylesheet.add_source(
|
||||
self.CSS, path=app_css_path, is_default_css=False
|
||||
)
|
||||
except Exception as error:
|
||||
self.on_exception(error)
|
||||
self._print_error_renderables()
|
||||
|
||||
@@ -571,7 +571,7 @@ class Styles(StylesBase):
|
||||
Args:
|
||||
specificity (Specificity3): A node specificity.
|
||||
is_default_rules (bool): True if the rules we're extracting are
|
||||
default (i.e. in Widget.CSS) rules. False if they're from user defined CSS.
|
||||
default (i.e. in Widget.DEFAULT_CSS) rules. False if they're from user defined CSS.
|
||||
|
||||
Returns:
|
||||
list[tuple[str, Specificity5, Any]]]: A list containing a tuple of <RULE NAME>, <SPECIFICITY> <RULE VALUE>.
|
||||
|
||||
@@ -555,7 +555,7 @@ if __name__ == "__main__":
|
||||
print(app.tree)
|
||||
print()
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
App > View {
|
||||
layout: dock;
|
||||
docks: sidebar=left | widgets=top;
|
||||
|
||||
@@ -14,7 +14,7 @@ Where the fear has gone there will be nothing. Only I will remain."""
|
||||
|
||||
|
||||
class BorderButtons(layout.Vertical):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
BorderButtons {
|
||||
dock: left;
|
||||
width: 24;
|
||||
@@ -34,7 +34,7 @@ class BorderButtons(layout.Vertical):
|
||||
class BorderApp(App):
|
||||
"""Demonstrates the border styles."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Static {
|
||||
margin: 2 4;
|
||||
padding: 2 4;
|
||||
|
||||
@@ -54,8 +54,8 @@ class NoParent(Exception):
|
||||
class DOMNode(MessagePump):
|
||||
"""The base class for object that can be in the Textual DOM (App and Widget)"""
|
||||
|
||||
# Custom CSS
|
||||
CSS: ClassVar[str] = ""
|
||||
# CSS defaults
|
||||
DEFAULT_CSS: ClassVar[str] = ""
|
||||
|
||||
# Default classes argument if not supplied
|
||||
DEFAULT_CLASSES: str = ""
|
||||
@@ -198,7 +198,7 @@ class DOMNode(MessagePump):
|
||||
return f"{base.__name__}"
|
||||
|
||||
for tie_breaker, base in enumerate(self._node_bases):
|
||||
css = base.CSS.strip()
|
||||
css = base.DEFAULT_CSS.strip()
|
||||
if css:
|
||||
css_stack.append((get_path(base), css, -tie_breaker))
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from .widget import Widget
|
||||
class Container(Widget):
|
||||
"""Simple container widget, with vertical layout."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Container {
|
||||
layout: vertical;
|
||||
overflow: auto;
|
||||
@@ -16,13 +16,13 @@ class Vertical(Container):
|
||||
"""A container widget to align children vertically."""
|
||||
|
||||
# Blank CSS is important, otherwise you get a clone of Container
|
||||
CSS = ""
|
||||
DEFAULT_CSS = ""
|
||||
|
||||
|
||||
class Horizontal(Container):
|
||||
"""A container widget to align children horizontally."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Horizontal {
|
||||
layout: horizontal;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class Horizontal(Container):
|
||||
class Center(Container):
|
||||
"""A container widget to align children in the center."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Center {
|
||||
layout: center;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ UPDATE_PERIOD: Final = 1 / 60
|
||||
class Screen(Widget):
|
||||
"""A widget for the root of the app."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Screen {
|
||||
layout: vertical;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -16,7 +16,7 @@ class ScrollView(Widget):
|
||||
|
||||
"""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
|
||||
ScrollView {
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -72,7 +72,7 @@ class Widget(DOMNode):
|
||||
|
||||
"""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Widget{
|
||||
scrollbar-background: $panel-darken-1;
|
||||
scrollbar-background-hover: $panel-darken-2;
|
||||
|
||||
@@ -29,7 +29,7 @@ class InvalidButtonVariant(Exception):
|
||||
class Button(Widget, can_focus=True):
|
||||
"""A simple clickable button."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Button {
|
||||
width: auto;
|
||||
min-width: 10;
|
||||
|
||||
@@ -106,7 +106,7 @@ class Coord(NamedTuple):
|
||||
|
||||
class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
DataTable {
|
||||
background: $surface;
|
||||
color: $text-surface;
|
||||
|
||||
@@ -13,7 +13,7 @@ from ..widget import Widget
|
||||
@rich.repr.auto
|
||||
class Footer(Widget):
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Footer {
|
||||
background: $accent;
|
||||
color: $text-accent;
|
||||
|
||||
@@ -11,7 +11,7 @@ from ..reactive import Reactive, watch
|
||||
class HeaderIcon(Widget):
|
||||
"""Display an 'icon' on the left of the header."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
HeaderIcon {
|
||||
dock: left;
|
||||
padding: 0 1;
|
||||
@@ -28,7 +28,7 @@ class HeaderIcon(Widget):
|
||||
class HeaderClock(Widget):
|
||||
"""Display a clock on the right of the header."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
HeaderClock {
|
||||
dock: right;
|
||||
width: auto;
|
||||
@@ -50,7 +50,7 @@ class HeaderClock(Widget):
|
||||
class HeaderTitle(Widget):
|
||||
"""Display the title / subtitle in the header."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
HeaderTitle {
|
||||
content-align: center middle;
|
||||
width: 100%;
|
||||
@@ -70,7 +70,7 @@ class HeaderTitle(Widget):
|
||||
class Header(Widget):
|
||||
"""A header widget with icon and clock."""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Header {
|
||||
dock: top;
|
||||
width: 100%;
|
||||
|
||||
@@ -7,7 +7,7 @@ from ..widget import Widget
|
||||
|
||||
|
||||
class Pretty(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Static {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ def _check_renderable(renderable: object):
|
||||
|
||||
|
||||
class Static(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Static {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ class TreeClick(Generic[NodeDataType], Message, bubble=True):
|
||||
|
||||
|
||||
class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
TreeControl {
|
||||
background: $panel;
|
||||
color: $text-panel;
|
||||
|
||||
@@ -112,7 +112,7 @@ class TextInput(TextWidgetBase, can_focus=True):
|
||||
suggestion will be displayed as dim text similar to suggestion text in the zsh or fish shells.
|
||||
"""
|
||||
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
TextInput {
|
||||
width: auto;
|
||||
background: $surface;
|
||||
@@ -417,7 +417,7 @@ class TextInput(TextWidgetBase, can_focus=True):
|
||||
|
||||
|
||||
class TextArea(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
TextArea { overflow: auto auto; height: 5; background: $primary-darken-1; }
|
||||
"""
|
||||
|
||||
@@ -428,7 +428,7 @@ class TextArea(Widget):
|
||||
class TextAreaChild(TextWidgetBase, can_focus=True):
|
||||
# TODO: Not nearly ready for prime-time, but it exists to help
|
||||
# model the superclass.
|
||||
CSS = "TextAreaChild { height: auto; background: $primary-darken-1; }"
|
||||
DEFAULT_CSS = "TextAreaChild { height: auto; background: $primary-darken-1; }"
|
||||
STOP_PROPAGATE = {"tab", "shift+tab"}
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
|
||||
@@ -106,7 +106,7 @@ def test_stylesheet_apply_user_css_over_widget_css():
|
||||
user_css = ".a {color: red; tint: yellow;}"
|
||||
|
||||
class MyWidget(Widget):
|
||||
CSS = ".a {color: blue !important; background: lime;}"
|
||||
DEFAULT_CSS = ".a {color: blue !important; background: lime;}"
|
||||
|
||||
node = MyWidget()
|
||||
node.add_class("a")
|
||||
|
||||
@@ -110,7 +110,7 @@ async def test_composition_of_vertical_container_with_children(
|
||||
expected_placeholders_offset_x: int,
|
||||
):
|
||||
class VerticalContainer(Widget):
|
||||
CSS = (
|
||||
DEFAULT_CSS = (
|
||||
"""
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
@@ -304,7 +304,7 @@ async def test_scrollbar_size_impact_on_the_layout(
|
||||
class LargeWidgetContainer(Widget):
|
||||
# TODO: Once textual#581 ("Default versus User CSS") is solved the following CSS should just use the
|
||||
# "LargeWidgetContainer" selector, without having to use a more specific one to be able to override Widget's CSS:
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
#large-widget-container {
|
||||
width: 20;
|
||||
height: 20;
|
||||
|
||||
@@ -48,7 +48,7 @@ async def test_scroll_to_widget(
|
||||
last_screen_expected_placeholder_ids: Sequence[int],
|
||||
):
|
||||
class VerticalContainer(Widget):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
VerticalContainer {
|
||||
layout: vertical;
|
||||
overflow: hidden auto;
|
||||
@@ -60,7 +60,7 @@ async def test_scroll_to_widget(
|
||||
"""
|
||||
|
||||
class MyTestApp(AppTest):
|
||||
CSS = """
|
||||
DEFAULT_CSS = """
|
||||
Placeholder {
|
||||
height: 5; /* minimal height to see the name of a Placeholder */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user