mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
psuedo classes
This commit is contained in:
@@ -4,6 +4,10 @@ App > View {
|
||||
docks: side=left/1;
|
||||
}
|
||||
|
||||
Widget:hover {
|
||||
outline: solid green;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
text: #09312e on #3caea3;
|
||||
dock: side;
|
||||
|
||||
@@ -239,6 +239,9 @@ class App(DOMNode):
|
||||
self.stylesheet.update(self)
|
||||
self.view.refresh(layout=True)
|
||||
|
||||
def update_styles(self) -> None:
|
||||
self.post_message_no_wait(messages.RefreshStyles(self))
|
||||
|
||||
def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
||||
self.register(self.view, *anon_widgets, **widgets)
|
||||
self.view.refresh()
|
||||
@@ -622,6 +625,11 @@ class App(DOMNode):
|
||||
self.view.query(selector).toggle_class(class_name)
|
||||
self.view.refresh(layout=True)
|
||||
|
||||
async def handle_refresh_styles(self, message: messages.RefreshStyles) -> None:
|
||||
self.reset_styles()
|
||||
self.stylesheet.update(self)
|
||||
self.view.refresh(layout=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
|
||||
@@ -45,15 +45,15 @@ class Selector:
|
||||
|
||||
@property
|
||||
def css(self) -> str:
|
||||
psuedo_suffix = "".join(f":{name}" for name in self.pseudo_classes)
|
||||
pseudo_suffix = "".join(f":{name}" for name in self.pseudo_classes)
|
||||
if self.type == SelectorType.UNIVERSAL:
|
||||
return "*"
|
||||
elif self.type == SelectorType.TYPE:
|
||||
return f"{self.name}{psuedo_suffix}"
|
||||
return f"{self.name}{pseudo_suffix}"
|
||||
elif self.type == SelectorType.CLASS:
|
||||
return f".{self.name}{psuedo_suffix}"
|
||||
return f".{self.name}{pseudo_suffix}"
|
||||
else:
|
||||
return f"#{self.name}{psuedo_suffix}"
|
||||
return f"#{self.name}{pseudo_suffix}"
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self._name_lower = self.name.lower()
|
||||
@@ -73,21 +73,21 @@ class Selector:
|
||||
def _check_type(self, node: DOMNode) -> bool:
|
||||
if node.css_type != self._name_lower:
|
||||
return False
|
||||
if self.pseudo_classes and not node.has_psuedo_class(*self.pseudo_classes):
|
||||
if self.pseudo_classes and not node.has_pseudo_class(*self.pseudo_classes):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_class(self, node: DOMNode) -> bool:
|
||||
if not node.has_class(self._name_lower):
|
||||
return False
|
||||
if self.pseudo_classes and not node.has_psuedo_class(*self.pseudo_classes):
|
||||
if self.pseudo_classes and not node.has_pseudo_class(*self.pseudo_classes):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_id(self, node: DOMNode) -> bool:
|
||||
if not node.id == self._name_lower:
|
||||
return False
|
||||
if self.pseudo_classes and not node.has_psuedo_class(*self.pseudo_classes):
|
||||
if self.pseudo_classes and not node.has_pseudo_class(*self.pseudo_classes):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -99,9 +99,11 @@ class DOMNode(MessagePump):
|
||||
return frozenset(self._classes)
|
||||
|
||||
@property
|
||||
def psuedo_classes(self) -> set[str]:
|
||||
"""Get a set of all psuedo classes"""
|
||||
return set()
|
||||
def pseudo_classes(self) -> frozenset[str]:
|
||||
"""Get a set of all pseudo classes"""
|
||||
pseudo_classes = frozenset({*self.get_pseudo_classes()})
|
||||
self.log(pseudo_classes)
|
||||
return pseudo_classes
|
||||
|
||||
@property
|
||||
def css_type(self) -> str:
|
||||
@@ -186,6 +188,9 @@ class DOMNode(MessagePump):
|
||||
add_children(tree, self)
|
||||
return tree
|
||||
|
||||
def get_pseudo_classes(self) -> Iterable[str]:
|
||||
return ()
|
||||
|
||||
def reset_styles(self) -> None:
|
||||
from .widget import Widget
|
||||
|
||||
@@ -255,7 +260,7 @@ class DOMNode(MessagePump):
|
||||
self._classes.symmetric_difference_update(class_names)
|
||||
self.app.stylesheet.update(self.app)
|
||||
|
||||
def has_psuedo_class(self, *class_names: str) -> bool:
|
||||
"""Check for psuedo class (such as hover, focus etc)"""
|
||||
has_psuedo_classes = self.psuedo_classes.issuperset(class_names)
|
||||
return has_psuedo_classes
|
||||
def has_pseudo_class(self, *class_names: str) -> bool:
|
||||
"""Check for pseudo class (such as hover, focus etc)"""
|
||||
has_pseudo_classes = self.pseudo_classes.issuperset(class_names)
|
||||
return has_pseudo_classes
|
||||
|
||||
@@ -66,7 +66,7 @@ class Load(Event, bubble=False):
|
||||
class Idle(Event, bubble=False):
|
||||
"""Sent when there are no more items in the message queue.
|
||||
|
||||
This is a psuedo-event in that it is created by the Textual system and doesn't go
|
||||
This is a pseudo-event in that it is created by the Textual system and doesn't go
|
||||
through the usual message queue.
|
||||
|
||||
"""
|
||||
|
||||
@@ -41,3 +41,12 @@ class CursorMove(Message):
|
||||
def __init__(self, sender: MessagePump, line: int) -> None:
|
||||
self.line = line
|
||||
super().__init__(sender)
|
||||
|
||||
|
||||
@rich.repr.auto
|
||||
class RefreshStyles(Message):
|
||||
def __init__(self, sender: MessagePump) -> None:
|
||||
super().__init__(sender)
|
||||
|
||||
def can_replace(self, message: Message) -> bool:
|
||||
return isinstance(message, RefreshStyles)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
from logging import PercentStyle, getLogger
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
@@ -77,6 +77,7 @@ class Widget(DOMNode):
|
||||
self._layout_required = False
|
||||
self._animate: BoundAnimator | None = None
|
||||
self._reactive_watches: dict[str, Callable] = {}
|
||||
self._mouse_over: bool = False
|
||||
self.render_cache: RenderCache | None = None
|
||||
self.highlight_style: Style | None = None
|
||||
|
||||
@@ -92,11 +93,19 @@ class Widget(DOMNode):
|
||||
yield "name", self.name
|
||||
if self.classes:
|
||||
yield "classes", self.classes
|
||||
pseudo_classes = self.pseudo_classes
|
||||
if pseudo_classes:
|
||||
yield "pseudo_classes", pseudo_classes
|
||||
yield "outline", self.styles.outline
|
||||
|
||||
def __rich__(self) -> RenderableType:
|
||||
renderable = self.render_styled()
|
||||
return renderable
|
||||
|
||||
def get_pseudo_classes(self) -> Iterable[str]:
|
||||
if self._mouse_over:
|
||||
yield "hover"
|
||||
|
||||
def get_child_by_id(self, id: str) -> Widget:
|
||||
"""Get a child with a given id.
|
||||
|
||||
@@ -212,6 +221,7 @@ class Widget(DOMNode):
|
||||
return gutter
|
||||
|
||||
def on_style_change(self) -> None:
|
||||
self.log("style_Change", self)
|
||||
self.clear_render_cache()
|
||||
|
||||
def _update_size(self, size: Size) -> None:
|
||||
@@ -359,3 +369,11 @@ class Widget(DOMNode):
|
||||
|
||||
async def on_click(self, event: events.Click) -> None:
|
||||
await self.broker_event("click", event)
|
||||
|
||||
async def on_enter(self, event: events.Enter) -> None:
|
||||
self._mouse_over = True
|
||||
self.app.update_styles()
|
||||
|
||||
async def on_leave(self, event: events.Leave) -> None:
|
||||
self._mouse_over = False
|
||||
self.app.update_styles()
|
||||
|
||||
Reference in New Issue
Block a user