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