Default css

This commit is contained in:
Will McGugan
2022-09-02 09:56:04 +01:00
parent b6728a694f
commit e555b8512f
33 changed files with 64 additions and 50 deletions

View File

@@ -7,7 +7,7 @@ from textual.widget import Widget
class Clock(Widget): class Clock(Widget):
"""A clock app.""" """A clock app."""
CSS = """ DEFAULT_CSS = """
Clock { Clock {
content-align: center middle; content-align: center middle;
} }

View File

@@ -4,7 +4,7 @@ from textual.widgets import Button
class ButtonApp(App): class ButtonApp(App):
CSS = """ DEFAULT_CSS = """
Button { Button {
width: 100%; width: 100%;
} }

View File

@@ -8,7 +8,7 @@ from textual.widgets import Placeholder
class VerticalContainer(Widget): class VerticalContainer(Widget):
CSS = """ DEFAULT_CSS = """
VerticalContainer { VerticalContainer {
layout: vertical; layout: vertical;
overflow: hidden auto; overflow: hidden auto;
@@ -24,7 +24,7 @@ class VerticalContainer(Widget):
class Introduction(Widget): class Introduction(Widget):
CSS = """ DEFAULT_CSS = """
Introduction { Introduction {
background: indigo; background: indigo;
color: white; color: white;

View File

@@ -23,7 +23,7 @@ class ColorDisplay(Widget, can_focus=True):
class ColorNames(App): class ColorNames(App):
CSS = """ DEFAULT_CSS = """
ColorDisplay { ColorDisplay {
height: 1; height: 1;
} }

View File

@@ -5,7 +5,7 @@ from textual.widget import Widget
class FiftyApp(App): class FiftyApp(App):
CSS = """ DEFAULT_CSS = """
Screen { Screen {
layout: vertical; layout: vertical;
} }
@@ -24,6 +24,7 @@ class FiftyApp(App):
yield layout.Horizontal(Widget(), Widget()) yield layout.Horizontal(Widget(), Widget())
yield layout.Horizontal(Widget(), Widget()) yield layout.Horizontal(Widget(), Widget())
app = FiftyApp() app = FiftyApp()
if __name__ == "__main__": if __name__ == "__main__":
app.run() app.run()

View File

@@ -9,7 +9,7 @@ placeholders_count = 12
class VerticalContainer(Widget): class VerticalContainer(Widget):
CSS = """ DEFAULT_CSS = """
VerticalContainer { VerticalContainer {
layout: vertical; layout: vertical;
overflow: hidden auto; overflow: hidden auto;
@@ -26,7 +26,7 @@ class VerticalContainer(Widget):
class Introduction(Widget): class Introduction(Widget):
CSS = """ DEFAULT_CSS = """
Introduction { Introduction {
background: indigo; background: indigo;
color: white; color: white;

View File

@@ -10,7 +10,7 @@ initial_placeholders_count = 4
class VerticalContainer(Widget): class VerticalContainer(Widget):
CSS = """ DEFAULT_CSS = """
VerticalContainer { VerticalContainer {
layout: vertical; layout: vertical;
overflow: hidden auto; overflow: hidden auto;
@@ -30,7 +30,7 @@ class VerticalContainer(Widget):
class Introduction(Widget): class Introduction(Widget):
CSS = """ DEFAULT_CSS = """
Introduction { Introduction {
background: indigo; background: indigo;
color: white; color: white;

View File

@@ -11,7 +11,7 @@ class Thing(Static):
class AddRemoveApp(App): class AddRemoveApp(App):
CSS = """ DEFAULT_CSS = """
#buttons { #buttons {
dock: top; dock: top;
height: auto; height: auto;

View File

@@ -3,7 +3,7 @@ from textual.widgets import Static
class CenterApp(App): class CenterApp(App):
CSS = """ DEFAULT_CSS = """
CenterApp Screen { CenterApp Screen {
layout: center; layout: center;

View File

@@ -4,7 +4,7 @@ from textual.widgets import Static
class CenterApp(App): class CenterApp(App):
CSS = """ DEFAULT_CSS = """
#sidebar { #sidebar {
dock: left; dock: left;

View File

@@ -10,7 +10,7 @@ from textual.widget import Widget
class Box(Widget, can_focus=True): class Box(Widget, can_focus=True):
CSS = "#box {background: blue;}" DEFAULT_CSS = "#box {background: blue;}"
def render(self) -> RenderableType: def render(self) -> RenderableType:
return Panel("Box") return Panel("Box")

View File

@@ -21,7 +21,7 @@ class NewScreen(Screen):
class ScreenApp(App): class ScreenApp(App):
CSS = """ DEFAULT_CSS = """
ScreenApp Screen { ScreenApp Screen {
background: #111144; background: #111144;
color: white; color: white;

View File

@@ -5,7 +5,7 @@ from textual.widgets import DirectoryTree
class TreeApp(App): class TreeApp(App):
CSS = """ DEFAULT_CSS = """
Screen { Screen {
overflow: auto; overflow: auto;

View File

@@ -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 { App {
background: $background; background: $background;
color: $text-background; color: $text-background;
@@ -683,7 +687,6 @@ class App(Generic[ReturnType], DOMNode):
async def _on_css_change(self) -> None: async def _on_css_change(self) -> None:
"""Called when the CSS changes (if watch_css is True).""" """Called when the CSS changes (if watch_css is True)."""
if self.css_path is not None: if self.css_path is not None:
try: try:
time = perf_counter() time = perf_counter()
stylesheet = self.stylesheet.copy() stylesheet = self.stylesheet.copy()
@@ -1060,6 +1063,16 @@ class App(Generic[ReturnType], DOMNode):
self.stylesheet.add_source( self.stylesheet.add_source(
css, path=path, is_default_css=True, tie_breaker=tie_breaker 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: except Exception as error:
self.on_exception(error) self.on_exception(error)
self._print_error_renderables() self._print_error_renderables()

View File

@@ -571,7 +571,7 @@ class Styles(StylesBase):
Args: Args:
specificity (Specificity3): A node specificity. specificity (Specificity3): A node specificity.
is_default_rules (bool): True if the rules we're extracting are 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: Returns:
list[tuple[str, Specificity5, Any]]]: A list containing a tuple of <RULE NAME>, <SPECIFICITY> <RULE VALUE>. list[tuple[str, Specificity5, Any]]]: A list containing a tuple of <RULE NAME>, <SPECIFICITY> <RULE VALUE>.

View File

@@ -555,7 +555,7 @@ if __name__ == "__main__":
print(app.tree) print(app.tree)
print() print()
CSS = """ DEFAULT_CSS = """
App > View { App > View {
layout: dock; layout: dock;
docks: sidebar=left | widgets=top; docks: sidebar=left | widgets=top;

View File

@@ -14,7 +14,7 @@ Where the fear has gone there will be nothing. Only I will remain."""
class BorderButtons(layout.Vertical): class BorderButtons(layout.Vertical):
CSS = """ DEFAULT_CSS = """
BorderButtons { BorderButtons {
dock: left; dock: left;
width: 24; width: 24;
@@ -34,7 +34,7 @@ class BorderButtons(layout.Vertical):
class BorderApp(App): class BorderApp(App):
"""Demonstrates the border styles.""" """Demonstrates the border styles."""
CSS = """ DEFAULT_CSS = """
Static { Static {
margin: 2 4; margin: 2 4;
padding: 2 4; padding: 2 4;

View File

@@ -54,8 +54,8 @@ class NoParent(Exception):
class DOMNode(MessagePump): class DOMNode(MessagePump):
"""The base class for object that can be in the Textual DOM (App and Widget)""" """The base class for object that can be in the Textual DOM (App and Widget)"""
# Custom CSS # CSS defaults
CSS: ClassVar[str] = "" DEFAULT_CSS: ClassVar[str] = ""
# Default classes argument if not supplied # Default classes argument if not supplied
DEFAULT_CLASSES: str = "" DEFAULT_CLASSES: str = ""
@@ -198,7 +198,7 @@ class DOMNode(MessagePump):
return f"{base.__name__}" return f"{base.__name__}"
for tie_breaker, base in enumerate(self._node_bases): for tie_breaker, base in enumerate(self._node_bases):
css = base.CSS.strip() css = base.DEFAULT_CSS.strip()
if css: if css:
css_stack.append((get_path(base), css, -tie_breaker)) css_stack.append((get_path(base), css, -tie_breaker))

View File

@@ -4,7 +4,7 @@ from .widget import Widget
class Container(Widget): class Container(Widget):
"""Simple container widget, with vertical layout.""" """Simple container widget, with vertical layout."""
CSS = """ DEFAULT_CSS = """
Container { Container {
layout: vertical; layout: vertical;
overflow: auto; overflow: auto;
@@ -16,13 +16,13 @@ class Vertical(Container):
"""A container widget to align children vertically.""" """A container widget to align children vertically."""
# Blank CSS is important, otherwise you get a clone of Container # Blank CSS is important, otherwise you get a clone of Container
CSS = "" DEFAULT_CSS = ""
class Horizontal(Container): class Horizontal(Container):
"""A container widget to align children horizontally.""" """A container widget to align children horizontally."""
CSS = """ DEFAULT_CSS = """
Horizontal { Horizontal {
layout: horizontal; layout: horizontal;
} }
@@ -32,7 +32,7 @@ class Horizontal(Container):
class Center(Container): class Center(Container):
"""A container widget to align children in the center.""" """A container widget to align children in the center."""
CSS = """ DEFAULT_CSS = """
Center { Center {
layout: center; layout: center;
} }

View File

@@ -29,7 +29,7 @@ UPDATE_PERIOD: Final = 1 / 60
class Screen(Widget): class Screen(Widget):
"""A widget for the root of the app.""" """A widget for the root of the app."""
CSS = """ DEFAULT_CSS = """
Screen { Screen {
layout: vertical; layout: vertical;
overflow-y: auto; overflow-y: auto;

View File

@@ -16,7 +16,7 @@ class ScrollView(Widget):
""" """
CSS = """ DEFAULT_CSS = """
ScrollView { ScrollView {
overflow-y: auto; overflow-y: auto;

View File

@@ -72,7 +72,7 @@ class Widget(DOMNode):
""" """
CSS = """ DEFAULT_CSS = """
Widget{ Widget{
scrollbar-background: $panel-darken-1; scrollbar-background: $panel-darken-1;
scrollbar-background-hover: $panel-darken-2; scrollbar-background-hover: $panel-darken-2;

View File

@@ -29,7 +29,7 @@ class InvalidButtonVariant(Exception):
class Button(Widget, can_focus=True): class Button(Widget, can_focus=True):
"""A simple clickable button.""" """A simple clickable button."""
CSS = """ DEFAULT_CSS = """
Button { Button {
width: auto; width: auto;
min-width: 10; min-width: 10;

View File

@@ -106,7 +106,7 @@ class Coord(NamedTuple):
class DataTable(ScrollView, Generic[CellType], can_focus=True): class DataTable(ScrollView, Generic[CellType], can_focus=True):
CSS = """ DEFAULT_CSS = """
DataTable { DataTable {
background: $surface; background: $surface;
color: $text-surface; color: $text-surface;

View File

@@ -13,7 +13,7 @@ from ..widget import Widget
@rich.repr.auto @rich.repr.auto
class Footer(Widget): class Footer(Widget):
CSS = """ DEFAULT_CSS = """
Footer { Footer {
background: $accent; background: $accent;
color: $text-accent; color: $text-accent;

View File

@@ -11,7 +11,7 @@ from ..reactive import Reactive, watch
class HeaderIcon(Widget): class HeaderIcon(Widget):
"""Display an 'icon' on the left of the header.""" """Display an 'icon' on the left of the header."""
CSS = """ DEFAULT_CSS = """
HeaderIcon { HeaderIcon {
dock: left; dock: left;
padding: 0 1; padding: 0 1;
@@ -28,7 +28,7 @@ class HeaderIcon(Widget):
class HeaderClock(Widget): class HeaderClock(Widget):
"""Display a clock on the right of the header.""" """Display a clock on the right of the header."""
CSS = """ DEFAULT_CSS = """
HeaderClock { HeaderClock {
dock: right; dock: right;
width: auto; width: auto;
@@ -50,7 +50,7 @@ class HeaderClock(Widget):
class HeaderTitle(Widget): class HeaderTitle(Widget):
"""Display the title / subtitle in the header.""" """Display the title / subtitle in the header."""
CSS = """ DEFAULT_CSS = """
HeaderTitle { HeaderTitle {
content-align: center middle; content-align: center middle;
width: 100%; width: 100%;
@@ -70,7 +70,7 @@ class HeaderTitle(Widget):
class Header(Widget): class Header(Widget):
"""A header widget with icon and clock.""" """A header widget with icon and clock."""
CSS = """ DEFAULT_CSS = """
Header { Header {
dock: top; dock: top;
width: 100%; width: 100%;

View File

@@ -7,7 +7,7 @@ from ..widget import Widget
class Pretty(Widget): class Pretty(Widget):
CSS = """ DEFAULT_CSS = """
Static { Static {
height: auto; height: auto;
} }

View File

@@ -25,7 +25,7 @@ def _check_renderable(renderable: object):
class Static(Widget): class Static(Widget):
CSS = """ DEFAULT_CSS = """
Static { Static {
height: auto; height: auto;
} }

View File

@@ -169,7 +169,7 @@ class TreeClick(Generic[NodeDataType], Message, bubble=True):
class TreeControl(Generic[NodeDataType], Widget, can_focus=True): class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
CSS = """ DEFAULT_CSS = """
TreeControl { TreeControl {
background: $panel; background: $panel;
color: $text-panel; color: $text-panel;

View File

@@ -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. suggestion will be displayed as dim text similar to suggestion text in the zsh or fish shells.
""" """
CSS = """ DEFAULT_CSS = """
TextInput { TextInput {
width: auto; width: auto;
background: $surface; background: $surface;
@@ -417,7 +417,7 @@ class TextInput(TextWidgetBase, can_focus=True):
class TextArea(Widget): class TextArea(Widget):
CSS = """ DEFAULT_CSS = """
TextArea { overflow: auto auto; height: 5; background: $primary-darken-1; } TextArea { overflow: auto auto; height: 5; background: $primary-darken-1; }
""" """
@@ -428,7 +428,7 @@ class TextArea(Widget):
class TextAreaChild(TextWidgetBase, can_focus=True): class TextAreaChild(TextWidgetBase, can_focus=True):
# TODO: Not nearly ready for prime-time, but it exists to help # TODO: Not nearly ready for prime-time, but it exists to help
# model the superclass. # 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"} STOP_PROPAGATE = {"tab", "shift+tab"}
def render(self) -> RenderableType: def render(self) -> RenderableType:

View File

@@ -106,7 +106,7 @@ def test_stylesheet_apply_user_css_over_widget_css():
user_css = ".a {color: red; tint: yellow;}" user_css = ".a {color: red; tint: yellow;}"
class MyWidget(Widget): class MyWidget(Widget):
CSS = ".a {color: blue !important; background: lime;}" DEFAULT_CSS = ".a {color: blue !important; background: lime;}"
node = MyWidget() node = MyWidget()
node.add_class("a") node.add_class("a")

View File

@@ -110,7 +110,7 @@ async def test_composition_of_vertical_container_with_children(
expected_placeholders_offset_x: int, expected_placeholders_offset_x: int,
): ):
class VerticalContainer(Widget): class VerticalContainer(Widget):
CSS = ( DEFAULT_CSS = (
""" """
VerticalContainer { VerticalContainer {
layout: vertical; layout: vertical;
@@ -304,7 +304,7 @@ async def test_scrollbar_size_impact_on_the_layout(
class LargeWidgetContainer(Widget): class LargeWidgetContainer(Widget):
# TODO: Once textual#581 ("Default versus User CSS") is solved the following CSS should just use the # 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: # "LargeWidgetContainer" selector, without having to use a more specific one to be able to override Widget's CSS:
CSS = """ DEFAULT_CSS = """
#large-widget-container { #large-widget-container {
width: 20; width: 20;
height: 20; height: 20;

View File

@@ -48,7 +48,7 @@ async def test_scroll_to_widget(
last_screen_expected_placeholder_ids: Sequence[int], last_screen_expected_placeholder_ids: Sequence[int],
): ):
class VerticalContainer(Widget): class VerticalContainer(Widget):
CSS = """ DEFAULT_CSS = """
VerticalContainer { VerticalContainer {
layout: vertical; layout: vertical;
overflow: hidden auto; overflow: hidden auto;
@@ -60,7 +60,7 @@ async def test_scroll_to_widget(
""" """
class MyTestApp(AppTest): class MyTestApp(AppTest):
CSS = """ DEFAULT_CSS = """
Placeholder { Placeholder {
height: 5; /* minimal height to see the name of a Placeholder */ height: 5; /* minimal height to see the name of a Placeholder */
} }