This commit is contained in:
Will McGugan
2021-11-19 17:01:42 +00:00
parent b47b0ac734
commit b5f3f367ac
9 changed files with 55 additions and 28 deletions

View File

@@ -17,7 +17,7 @@ class BasicApp(App):
}
#widget1 {
text: on blue;
text-background: blue;
dock-group: widgets;
}
@@ -30,10 +30,11 @@ class BasicApp(App):
async def on_mount(self) -> None:
"""Build layout here."""
self.log("MOUNT")
await self.view.mount(
sidebar=Placeholder(), widget1=Placeholder(), widget2=Placeholder()
)
self.log("MOUNTED CHILDREN", self.view.children)
BasicApp.run(log="textual.log")

View File

@@ -1,16 +1,18 @@
from typing import Any
from rich.console import RenderableType
__all__ = ["log", "panic"]
def log(*args: Any, verbosity: int = 0, **kwargs) -> None:
def log(*args: object, verbosity: int = 0, **kwargs) -> None:
from ._context import active_app
app = active_app.get()
app.log(*args, verbosity=verbosity, **kwargs)
def panic(*args: Any) -> None:
def panic(*args: RenderableType) -> None:
from ._context import active_app
app = active_app.get()

View File

@@ -30,6 +30,7 @@ from ._event_broker import extract_handler_actions, NoHandler
from .driver import Driver
from .layouts.dock import DockLayout, Dock
from ._linux_driver import LinuxDriver
from ._types import MessageTarget
from .message_pump import MessagePump
from ._profile import timer
from .view import View
@@ -53,6 +54,10 @@ else:
uvloop.install()
class AppError(Exception):
pass
class ActionError(Exception):
pass
@@ -90,7 +95,7 @@ class App(DOMNode):
self.driver_class = driver_class or LinuxDriver
self._title = title
self._layout = DockLayout()
self._view_stack: list[DockView] = []
self._view_stack: list[View] = []
self.focused: Widget | None = None
self.mouse_over: Widget | None = None
@@ -211,7 +216,6 @@ class App(DOMNode):
asyncio.run(run_app())
async def push_view(self, view: ViewType) -> ViewType:
self.register(view, self)
self._view_stack.append(view)
return view
@@ -311,17 +315,19 @@ class App(DOMNode):
self._print_error_renderables()
return
self._running = True
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))
# Wait for the load event to be processed, so we don't go in to application mode beforehand
await load_event.wait()
await self.post_message(events.Mount(sender=self))
view = DockView()
await self.mount(self, view)
await self.push_view(view)
driver = self._driver = self.driver_class(self.console, self)
driver.start_application_mode()
@@ -340,31 +346,42 @@ class App(DOMNode):
except:
self.panic()
finally:
self._running = False
if self._exit_renderables:
self._print_error_renderables()
if self.log_file is not None:
self.log_file.close()
def register(self, child: DOMNode, parent: DOMNode) -> bool:
def _register(self, parent: DOMNode, child: DOMNode) -> bool:
if child not in self.registry:
parent.children._append(child)
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:
async def mount(
self, parent: DOMNode, *anon_widgets: Widget, **widgets: Widget
) -> None:
"""Mount widget(s) so they may receive events.
Args:
parent (Widget): Parent Widget
"""
if not anon_widgets and not widgets:
raise AppError(
"Nothing to mount, did you forget parent as first positional arg?"
)
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)
if widget not in self.registry:
if widget_id is not None:
widget.id = widget_id
apply_stylesheet(widget)
widget.post_message_no_wait(events.Mount(sender=parent))
def is_mounted(self, widget: Widget) -> bool:
return widget in self.registry

View File

@@ -174,7 +174,7 @@ class StyleProperty:
def __set_name__(self, owner: Styles, name: str) -> None:
self._color_name = f"_rule_{name}_color"
self._bgcolor_name = f"_rule_{name}_bgcolor"
self._bgcolor_name = f"_rule_{name}_background"
self._style_name = f"_rule_{name}_style"
def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> Style:

View File

@@ -260,11 +260,11 @@ class StylesBuilder:
name, token, f"unexpected token {token.value!r} in declaration"
)
def process_text_bgcolor(self, name: str, tokens: list[Token]) -> None:
def process_text_background(self, name: str, tokens: list[Token]) -> None:
for token in tokens:
if token.name in ("color", "token"):
try:
self.styles._rule_text_bgcolor = Color.parse(token.value)
self.styles._rule_text_background = Color.parse(token.value)
except Exception as error:
self.error(
name, token, f"failed to parse color {token.value!r}; {error}"

View File

@@ -37,6 +37,7 @@ from ._style_properties import (
from .types import Display, Visibility
@rich.repr.auto
@dataclass
class Styles:
@@ -45,7 +46,7 @@ class Styles:
_rule_layout: str | None = None
_rule_text_color: Color | None = None
_rule_text_bgcolor: Color | None = None
_rule_text_background: Color | None = None
_rule_text_style: Style | None = None
_rule_padding: Spacing | None = None
@@ -84,7 +85,7 @@ class Styles:
text = StyleProperty()
text_color = ColorProperty()
text_bgcolor = ColorProperty()
text_background = ColorProperty()
text_style = StyleFlagsProperty()
padding = SpacingProperty()
@@ -250,7 +251,7 @@ class Styles:
append_declaration("layers", " ".join(self.layers))
if self._rule_layer is not None:
append_declaration("layer", self.layer)
if self._rule_text_color or self._rule_text_bgcolor or self._rule_text_style:
if self._rule_text_color or self._rule_text_background or self._rule_text_style:
append_declaration("text", str(self.text))
if self._rule_width is not None:

View File

@@ -168,7 +168,9 @@ class Layout(ABC):
"""
async def mount_all(self, view: "View") -> None:
await view.mount(*self.get_widgets(view))
widgets = list(self.get_widgets(view))
if widgets:
await view.mount(*widgets)
@property
def map(self) -> LayoutMap | None:

View File

@@ -5,8 +5,8 @@ from collections import defaultdict
from dataclasses import dataclass
from typing import Iterable, TYPE_CHECKING, Sequence
from ..app import active_app
from .. import log
from ..dom import DOMNode
from .._layout_resolve import layout_resolve
from ..geometry import Offset, Region, Size
@@ -48,14 +48,18 @@ class DockLayout(Layout):
self._docks: list[Dock] | None = None
def get_docks(self, view: View) -> list[Dock]:
log("CHILDREN", view.children)
groups: dict[str, list[Widget]] = defaultdict(list)
for child in view.children:
assert isinstance(child, Widget)
groups[child.styles.dock_group].append(child)
log("GROUPS", groups)
docks: list[Dock] = []
append_dock = docks.append
log("STYLES.DOCKS", view.styles)
for name, edge in view.styles.docks:
append_dock(Dock(edge, groups[name], 0))
log("DOCKS", docks)
return docks
def get_widgets(self, view: View) -> Iterable[DOMNode]:

View File

@@ -138,7 +138,7 @@ class View(Widget):
self.app.refresh()
async def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
await self.app.mount(*anon_widgets, **widgets)
await self.app.mount(self, *anon_widgets, **widgets)
self.refresh()
async def refresh_layout(self) -> None: