mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
ws
This commit is contained in:
@@ -7,7 +7,7 @@ class BasicApp(App):
|
|||||||
|
|
||||||
css = """
|
css = """
|
||||||
|
|
||||||
App > View {
|
App > DockView {
|
||||||
layout: dock;
|
layout: dock;
|
||||||
docks: sidebar=left | widgets=top;
|
docks: sidebar=left | widgets=top;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from weakref import ref
|
|||||||
import rich.repr
|
import rich.repr
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .widget import Widget
|
|
||||||
from .dom import DOMNode
|
from .dom import DOMNode
|
||||||
|
|
||||||
|
|
||||||
@@ -20,8 +19,8 @@ class NodeList:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._widget_refs: list[ref[DOMNode]] = []
|
self._node_refs: list[ref[DOMNode]] = []
|
||||||
self.__widgets: list[DOMNode] | None = []
|
self.__nodes: list[DOMNode] | None = []
|
||||||
|
|
||||||
def __rich_repr__(self) -> rich.repr.Result:
|
def __rich_repr__(self) -> rich.repr.Result:
|
||||||
yield self._widgets
|
yield self._widgets
|
||||||
@@ -29,38 +28,38 @@ class NodeList:
|
|||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return len(self._widgets)
|
return len(self._widgets)
|
||||||
|
|
||||||
def __contains__(self, widget: Widget) -> bool:
|
def __contains__(self, widget: DOMNode) -> bool:
|
||||||
return widget in self._widgets
|
return widget in self._widgets
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _widgets(self) -> list[DOMNode]:
|
def _widgets(self) -> list[DOMNode]:
|
||||||
if self.__widgets is None:
|
if self.__nodes is None:
|
||||||
self.__widgets = list(
|
self.__nodes = list(
|
||||||
filter(None, [widget_ref() for widget_ref in self._widget_refs])
|
filter(None, [widget_ref() for widget_ref in self._node_refs])
|
||||||
)
|
)
|
||||||
return self.__widgets
|
return self.__nodes
|
||||||
|
|
||||||
def _prune(self) -> None:
|
def _prune(self) -> None:
|
||||||
"""Remove expired references."""
|
"""Remove expired references."""
|
||||||
self._widget_refs[:] = filter(
|
self._node_refs[:] = filter(
|
||||||
None,
|
None,
|
||||||
[
|
[
|
||||||
None if widget_ref() is None else widget_ref
|
None if widget_ref() is None else widget_ref
|
||||||
for widget_ref in self._widget_refs
|
for widget_ref in self._node_refs
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def _append(self, widget: DOMNode) -> None:
|
def _append(self, widget: DOMNode) -> None:
|
||||||
if widget not in self._widgets:
|
if widget not in self._widgets:
|
||||||
self._widget_refs.append(ref(widget))
|
self._node_refs.append(ref(widget))
|
||||||
self.__widgets = None
|
self.__nodes = None
|
||||||
|
|
||||||
def _clear(self) -> None:
|
def _clear(self) -> None:
|
||||||
del self._widget_refs[:]
|
del self._node_refs[:]
|
||||||
self.__widgets = None
|
self.__nodes = None
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[DOMNode]:
|
def __iter__(self) -> Iterator[DOMNode]:
|
||||||
for widget_ref in self._widget_refs:
|
for widget_ref in self._node_refs:
|
||||||
widget = widget_ref()
|
widget = widget_ref()
|
||||||
if widget is not None:
|
if widget is not None:
|
||||||
yield widget
|
yield widget
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import os
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from typing import Any, Callable, ClassVar, Type, TypeVar
|
from typing import Any, Callable, ClassVar, Iterable, Type, TypeVar
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from rich.control import Control
|
from rich.control import Control
|
||||||
@@ -301,6 +301,7 @@ class App(DOMNode):
|
|||||||
self.stylesheet.read(self.css_file)
|
self.stylesheet.read(self.css_file)
|
||||||
if self.css is not None:
|
if self.css is not None:
|
||||||
self.stylesheet.parse(self.css, path=f"<{self.__class__.__name__}>")
|
self.stylesheet.parse(self.css, path=f"<{self.__class__.__name__}>")
|
||||||
|
print(self.stylesheet.css)
|
||||||
except StylesheetParseError as error:
|
except StylesheetParseError as error:
|
||||||
self.panic(error)
|
self.panic(error)
|
||||||
self._print_error_renderables()
|
self._print_error_renderables()
|
||||||
@@ -313,8 +314,10 @@ class App(DOMNode):
|
|||||||
try:
|
try:
|
||||||
load_event = events.Load(sender=self)
|
load_event = events.Load(sender=self)
|
||||||
await self.dispatch_message(load_event)
|
await self.dispatch_message(load_event)
|
||||||
|
view = DockView()
|
||||||
|
await self.mount(view)
|
||||||
|
await self.push_view(view)
|
||||||
await self.post_message(events.Mount(self))
|
await self.post_message(events.Mount(self))
|
||||||
await self.push_view(DockView())
|
|
||||||
|
|
||||||
# Wait for the load event to be processed, so we don't go in to application mode beforehand
|
# Wait for the load event to be processed, so we don't go in to application mode beforehand
|
||||||
await load_event.wait()
|
await load_event.wait()
|
||||||
@@ -342,15 +345,27 @@ class App(DOMNode):
|
|||||||
if self.log_file is not None:
|
if self.log_file is not None:
|
||||||
self.log_file.close()
|
self.log_file.close()
|
||||||
|
|
||||||
def register(self, child: MessagePump, parent: MessagePump) -> bool:
|
def register(self, child: DOMNode, parent: DOMNode) -> bool:
|
||||||
if child not in self.registry:
|
if child not in self.registry:
|
||||||
self.registry.add(child)
|
self.registry.add(child)
|
||||||
child.set_parent(parent)
|
child.set_parent(parent)
|
||||||
child.start_messages()
|
child.start_messages()
|
||||||
child.post_message_no_wait(events.Mount(sender=parent))
|
child.post_message_no_wait(events.Mount(sender=parent))
|
||||||
|
parent.children._append(child)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
||||||
|
|
||||||
|
name_widgets: Iterable[tuple[str | None, Widget]]
|
||||||
|
name_widgets = [*((None, widget) for widget in anon_widgets), *widgets.items()]
|
||||||
|
apply_stylesheet = self.stylesheet.apply
|
||||||
|
for widget_id, widget in name_widgets:
|
||||||
|
if widget_id is not None:
|
||||||
|
widget.id = widget_id
|
||||||
|
self.register(widget, self)
|
||||||
|
apply_stylesheet(widget)
|
||||||
|
|
||||||
def is_mounted(self, widget: Widget) -> bool:
|
def is_mounted(self, widget: Widget) -> bool:
|
||||||
return widget in self.registry
|
return widget in self.registry
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ from .match import match
|
|||||||
from .parse import parse_selectors
|
from .parse import parse_selectors
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..dom import DOMNode
|
from ..widget import Widget
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto(angular=True)
|
@rich.repr.auto(angular=True)
|
||||||
class DOMQuery:
|
class DOMQuery:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
node: DOMNode | None = None,
|
node: Widget | None = None,
|
||||||
selector: str | None = None,
|
selector: str | None = None,
|
||||||
nodes: Iterable[DOMNode] | None = None,
|
nodes: Iterable[Widget] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
self._nodes: list[DOMNode]
|
self._nodes: list[Widget] = []
|
||||||
if nodes is not None:
|
if nodes is not None:
|
||||||
self._nodes = list(nodes)
|
self._nodes = list(nodes)
|
||||||
elif node is not None:
|
elif node is not None:
|
||||||
@@ -34,7 +34,7 @@ class DOMQuery:
|
|||||||
selector_set = parse_selectors(selector)
|
selector_set = parse_selectors(selector)
|
||||||
self._nodes = [_node for _node in self._nodes if match(selector_set, _node)]
|
self._nodes = [_node for _node in self._nodes if match(selector_set, _node)]
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[DOMNode]:
|
def __iter__(self) -> Iterator[Widget]:
|
||||||
return iter(self._nodes)
|
return iter(self._nodes)
|
||||||
|
|
||||||
def __rich_repr__(self) -> rich.repr.Result:
|
def __rich_repr__(self) -> rich.repr.Result:
|
||||||
@@ -45,3 +45,6 @@ class DOMQuery:
|
|||||||
query = DOMQuery()
|
query = DOMQuery()
|
||||||
query._nodes = [_node for _node in self._nodes if match(selector_set, _node)]
|
query._nodes = [_node for _node in self._nodes if match(selector_set, _node)]
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
def first(self) -> Widget:
|
||||||
|
return self._nodes[0]
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from .model import RuleSet
|
|||||||
from .parse import parse
|
from .parse import parse
|
||||||
from .types import Specificity3, Specificity4
|
from .types import Specificity3, Specificity4
|
||||||
from ..dom import DOMNode
|
from ..dom import DOMNode
|
||||||
|
from .. import log
|
||||||
|
|
||||||
|
|
||||||
class StylesheetParseError(Exception):
|
class StylesheetParseError(Exception):
|
||||||
@@ -133,6 +134,7 @@ class Stylesheet:
|
|||||||
for name, specificity_rules in rule_attributes.items()
|
for name, specificity_rules in rule_attributes.items()
|
||||||
]
|
]
|
||||||
node.styles.apply_rules(node_rules)
|
node.styles.apply_rules(node_rules)
|
||||||
|
log(node, node_rules)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, Iterator, TYPE_CHECKING
|
from typing import cast, Iterable, Iterator, TYPE_CHECKING
|
||||||
|
|
||||||
from rich.highlighter import ReprHighlighter
|
from rich.highlighter import ReprHighlighter
|
||||||
import rich.repr
|
import rich.repr
|
||||||
@@ -14,6 +14,7 @@ from ._node_list import NodeList
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .css.query import DOMQuery
|
from .css.query import DOMQuery
|
||||||
|
from .widget import Widget
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ from rich.segment import Segment, SegmentLines
|
|||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
from . import log, panic
|
from . import log, panic
|
||||||
from .dom import DOMNode
|
|
||||||
from ._loop import loop_last
|
from ._loop import loop_last
|
||||||
from .layout_map import LayoutMap
|
from .layout_map import LayoutMap
|
||||||
from ._profile import timer
|
from ._profile import timer
|
||||||
@@ -150,7 +149,7 @@ class Layout(ABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_widgets(self, view: View) -> Iterable[DOMNode]:
|
def get_widgets(self, view: View) -> Iterable[Widget]:
|
||||||
...
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ from collections import defaultdict
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Iterable, TYPE_CHECKING, Sequence
|
from typing import Iterable, TYPE_CHECKING, Sequence
|
||||||
|
|
||||||
|
from ..app import active_app
|
||||||
|
|
||||||
from ..dom import DOMNode
|
from ..dom import DOMNode
|
||||||
from .._layout_resolve import layout_resolve
|
from .._layout_resolve import layout_resolve
|
||||||
from ..geometry import Offset, Region, Size
|
from ..geometry import Offset, Region, Size
|
||||||
from ..layout import Layout, WidgetPlacement
|
from ..layout import Layout, WidgetPlacement
|
||||||
from ..layout_map import LayoutMap
|
from ..layout_map import LayoutMap
|
||||||
|
from ..widget import Widget
|
||||||
|
|
||||||
if sys.version_info >= (3, 8):
|
if sys.version_info >= (3, 8):
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
@@ -35,7 +38,7 @@ class DockOptions:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Dock:
|
class Dock:
|
||||||
edge: str
|
edge: str
|
||||||
widgets: Sequence[DOMNode]
|
widgets: Sequence[Widget]
|
||||||
z: int = 0
|
z: int = 0
|
||||||
|
|
||||||
|
|
||||||
@@ -45,8 +48,9 @@ class DockLayout(Layout):
|
|||||||
self._docks: list[Dock] | None = None
|
self._docks: list[Dock] | None = None
|
||||||
|
|
||||||
def get_docks(self, view: View) -> list[Dock]:
|
def get_docks(self, view: View) -> list[Dock]:
|
||||||
groups: dict[str, list[DOMNode]] = defaultdict(list)
|
groups: dict[str, list[Widget]] = defaultdict(list)
|
||||||
for child in view.children:
|
for child in view.children:
|
||||||
|
assert isinstance(child, Widget)
|
||||||
groups[child.styles.dock_group].append(child)
|
groups[child.styles.dock_group].append(child)
|
||||||
docks: list[Dock] = []
|
docks: list[Dock] = []
|
||||||
append_dock = docks.append
|
append_dock = docks.append
|
||||||
|
|||||||
@@ -138,15 +138,7 @@ class View(Widget):
|
|||||||
self.app.refresh()
|
self.app.refresh()
|
||||||
|
|
||||||
async def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
async def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
||||||
|
await self.app.mount(*anon_widgets, **widgets)
|
||||||
name_widgets: Iterable[tuple[str | None, Widget]]
|
|
||||||
name_widgets = [*((None, widget) for widget in anon_widgets), *widgets.items()]
|
|
||||||
apply_stylesheet = self.app.stylesheet.apply
|
|
||||||
for widget_id, widget in name_widgets:
|
|
||||||
if widget_id is not None:
|
|
||||||
widget.id = widget_id
|
|
||||||
apply_stylesheet(widget)
|
|
||||||
self._add_child(widget)
|
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
async def refresh_layout(self) -> None:
|
async def refresh_layout(self) -> None:
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from typing import (
|
|||||||
Callable,
|
Callable,
|
||||||
ClassVar,
|
ClassVar,
|
||||||
NamedTuple,
|
NamedTuple,
|
||||||
NewType,
|
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
import rich.repr
|
import rich.repr
|
||||||
@@ -116,25 +115,15 @@ class Widget(DOMNode):
|
|||||||
renderable = self.render_styled()
|
renderable = self.render_styled()
|
||||||
return renderable
|
return renderable
|
||||||
|
|
||||||
def _add_child(self, widget: Widget) -> Widget:
|
|
||||||
"""Add a child widget.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
widget (Widget): Widget
|
|
||||||
"""
|
|
||||||
self.app.register(widget, self)
|
|
||||||
self.children._append(widget)
|
|
||||||
return widget
|
|
||||||
|
|
||||||
def get_child(self, name: str | None = None, id: str | None = None) -> Widget:
|
def get_child(self, name: str | None = None, id: str | None = None) -> Widget:
|
||||||
if name is not None:
|
if name is not None:
|
||||||
for widget in self.children:
|
for widget in self.children:
|
||||||
if widget.name == name:
|
if widget.name == name:
|
||||||
return widget
|
return cast(Widget, widget)
|
||||||
if id is not None:
|
if id is not None:
|
||||||
for widget in self.children:
|
for widget in self.children:
|
||||||
if widget.id == id:
|
if widget.id == id:
|
||||||
return widget
|
return cast(Widget, widget)
|
||||||
raise errors.MissingWidget(f"Widget named {name!r} was not found in {self}")
|
raise errors.MissingWidget(f"Widget named {name!r} was not found in {self}")
|
||||||
|
|
||||||
def watch(self, attribute_name, callback: Callable[[Any], Awaitable[None]]) -> None:
|
def watch(self, attribute_name, callback: Callable[[Any], Awaitable[None]]) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user