mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
css updates
This commit is contained in:
@@ -1,39 +1,39 @@
|
|||||||
App > View {
|
App > View {
|
||||||
layout: dock;
|
layout: dock;
|
||||||
docks: side=left/1 header=top footer=bottom;
|
docks: side=left/1 header=top footer=bottom;
|
||||||
layers: base panels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
text: bold #09312e on #3caea3;
|
text: bold #09312e on #3caea3;
|
||||||
dock-group: side;
|
dock: side;
|
||||||
width: 30;
|
width: 30;
|
||||||
height: 1fr;
|
height: 1fr;
|
||||||
layer: panels;
|
|
||||||
border-right: outer #09312e;
|
border-right: outer #09312e;
|
||||||
offset-x: -100%;
|
offset-x: -100%;
|
||||||
transition: offset 1.2s in_cubic 200ms;
|
transition: offset 400ms in_out_cubic;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar.-active {
|
#sidebar.-active {
|
||||||
offset-x: 0;
|
offset-x: 0;
|
||||||
|
transition: offset 400ms in_out_cubic;
|
||||||
|
text: on red;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header {
|
#header {
|
||||||
text: on #173f5f;
|
text: on #173f5f;
|
||||||
dock-group: header;
|
dock: header;
|
||||||
height: 3;
|
height: 3;
|
||||||
border: hkey white;
|
border: hkey white;
|
||||||
}
|
}
|
||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
dock-group: header;
|
dock: header;
|
||||||
height: 3;
|
height: 3;
|
||||||
border-top: hkey #0f2b41;
|
border-top: hkey #0f2b41;
|
||||||
text: #3a3009 on #f6d55c;
|
text: #3a3009 on #f6d55c;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
dock-group: header;
|
dock: header;
|
||||||
text: on #20639b;
|
text: bold on #20639b;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class BasicApp(App):
|
|||||||
"""A basic app demonstrating CSS"""
|
"""A basic app demonstrating CSS"""
|
||||||
|
|
||||||
def on_load(self):
|
def on_load(self):
|
||||||
|
"""Bind keys here."""
|
||||||
self.bind("tab", "toggle_class('#sidebar', '-active')")
|
self.bind("tab", "toggle_class('#sidebar', '-active')")
|
||||||
|
|
||||||
def on_mount(self):
|
def on_mount(self):
|
||||||
@@ -18,4 +19,4 @@ class BasicApp(App):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
BasicApp.run(log="textual.log", css_file="basic.css", watch_css=True)
|
BasicApp.run(css_file="basic.css", watch_css=True)
|
||||||
|
|||||||
38
examples/example.css
Normal file
38
examples/example.css
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
App > View {
|
||||||
|
layout: dock;
|
||||||
|
docks: side=left/1 header=top footer=bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
text: bold #09312e on #3caea3;
|
||||||
|
dock: side;
|
||||||
|
width: 30;
|
||||||
|
height: 1fr;
|
||||||
|
border-right: outer #09312e;
|
||||||
|
offset-x: -100%;
|
||||||
|
transition: offset 400ms in_out_cubic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar.-active {
|
||||||
|
offset-x: 0;
|
||||||
|
transition: offset 400ms in_out_cubic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
text: on #173f5f;
|
||||||
|
dock: header;
|
||||||
|
height: 3;
|
||||||
|
border: hkey white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
dock: header;
|
||||||
|
height: 3;
|
||||||
|
border-top: hkey #0f2b41;
|
||||||
|
text: #3a3009 on #f6d55c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
dock: header;
|
||||||
|
text: bold on #20639b;
|
||||||
|
}
|
||||||
@@ -4,8 +4,7 @@ from abc import ABC, abstractmethod
|
|||||||
import asyncio
|
import asyncio
|
||||||
import sys
|
import sys
|
||||||
from time import time
|
from time import time
|
||||||
from tracemalloc import start
|
from typing import Any, Callable, TypeVar
|
||||||
from typing import Callable, TypeVar
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
@@ -62,7 +61,6 @@ class SimpleAnimation(Animation):
|
|||||||
factor = min(1.0, (time - self.start_time) / self.duration)
|
factor = min(1.0, (time - self.start_time) / self.duration)
|
||||||
eased_factor = self.easing(factor)
|
eased_factor = self.easing(factor)
|
||||||
|
|
||||||
log("ANIMATE", self.start_value, self.end_value)
|
|
||||||
if isinstance(self.start_value, Animatable):
|
if isinstance(self.start_value, Animatable):
|
||||||
assert isinstance(
|
assert isinstance(
|
||||||
self.end_value, Animatable, "end_value must be animatable"
|
self.end_value, Animatable, "end_value must be animatable"
|
||||||
@@ -117,6 +115,7 @@ class BoundAnimator:
|
|||||||
class Animator:
|
class Animator:
|
||||||
def __init__(self, target: MessageTarget, frames_per_second: int = 60) -> None:
|
def __init__(self, target: MessageTarget, frames_per_second: int = 60) -> None:
|
||||||
self._animations: dict[tuple[object, str], SimpleAnimation] = {}
|
self._animations: dict[tuple[object, str], SimpleAnimation] = {}
|
||||||
|
self.target = target
|
||||||
self._timer = Timer(
|
self._timer = Timer(
|
||||||
target,
|
target,
|
||||||
1 / frames_per_second,
|
1 / frames_per_second,
|
||||||
@@ -144,13 +143,13 @@ class Animator:
|
|||||||
self,
|
self,
|
||||||
obj: object,
|
obj: object,
|
||||||
attribute: str,
|
attribute: str,
|
||||||
value: float,
|
value: Any,
|
||||||
*,
|
*,
|
||||||
duration: float | None = None,
|
duration: float | None = None,
|
||||||
speed: float | None = None,
|
speed: float | None = None,
|
||||||
easing: EasingFunction | str = DEFAULT_EASING,
|
easing: EasingFunction | str = DEFAULT_EASING,
|
||||||
) -> None:
|
) -> None:
|
||||||
log("animate", obj, attribute, value)
|
|
||||||
start_time = time()
|
start_time = time()
|
||||||
|
|
||||||
animation_key = (id(obj), attribute)
|
animation_key = (id(obj), attribute)
|
||||||
@@ -167,7 +166,7 @@ class Animator:
|
|||||||
start_time,
|
start_time,
|
||||||
duration=duration,
|
duration=duration,
|
||||||
speed=speed,
|
speed=speed,
|
||||||
easing=easing,
|
easing=easing_function,
|
||||||
)
|
)
|
||||||
|
|
||||||
if animation is None:
|
if animation is None:
|
||||||
@@ -206,3 +205,4 @@ class Animator:
|
|||||||
animation = self._animations[animation_key]
|
animation = self._animations[animation_key]
|
||||||
if animation(animation_time):
|
if animation(animation_time):
|
||||||
del self._animations[animation_key]
|
del self._animations[animation_key]
|
||||||
|
self.target.view.refresh(True, True)
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ class LinuxDriver(Driver):
|
|||||||
if not self.exit_event.is_set():
|
if not self.exit_event.is_set():
|
||||||
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
|
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
|
||||||
self._disable_mouse_support()
|
self._disable_mouse_support()
|
||||||
|
termios.tcflush(self.fileno, termios.TCIFLUSH)
|
||||||
self.exit_event.set()
|
self.exit_event.set()
|
||||||
if self._key_thread is not None:
|
if self._key_thread is not None:
|
||||||
self._key_thread.join()
|
self._key_thread.join()
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from typing import (
|
|||||||
TypeVar,
|
TypeVar,
|
||||||
Generic,
|
Generic,
|
||||||
Union,
|
Union,
|
||||||
Iterator,
|
|
||||||
Iterable,
|
Iterable,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ class App(DOMNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self) -> Size:
|
def size(self) -> Size:
|
||||||
width, height = self.console.size
|
|
||||||
return Size(*self.console.size)
|
return Size(*self.console.size)
|
||||||
|
|
||||||
def log(self, *args: Any, verbosity: int = 1, **kwargs) -> None:
|
def log(self, *args: Any, verbosity: int = 1, **kwargs) -> None:
|
||||||
@@ -240,7 +239,7 @@ class App(DOMNode):
|
|||||||
self.reset_styles()
|
self.reset_styles()
|
||||||
self.stylesheet = stylesheet
|
self.stylesheet = stylesheet
|
||||||
self.stylesheet.update(self)
|
self.stylesheet.update(self)
|
||||||
self.view.refresh(layout=True)
|
self.refresh(layout=True)
|
||||||
|
|
||||||
def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
|
||||||
self.register(self.view, *anon_widgets, **widgets)
|
self.register(self.view, *anon_widgets, **widgets)
|
||||||
|
|||||||
@@ -259,13 +259,13 @@ class DocksProperty:
|
|||||||
return docks
|
return docks
|
||||||
|
|
||||||
|
|
||||||
class DockGroupProperty:
|
class DockProperty:
|
||||||
def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> str:
|
def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> str:
|
||||||
return obj._rule_dock_group or ""
|
return obj._rule_dock or ""
|
||||||
|
|
||||||
def __set__(self, obj: Styles, spacing: str | None) -> str | None:
|
def __set__(self, obj: Styles, spacing: str | None) -> str | None:
|
||||||
obj.refresh(True)
|
obj.refresh(True)
|
||||||
obj._rule_dock_group = spacing
|
obj._rule_dock = spacing
|
||||||
return spacing
|
return spacing
|
||||||
|
|
||||||
|
|
||||||
@@ -279,9 +279,12 @@ class OffsetProperty:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __set__(
|
def __set__(
|
||||||
self, obj: Styles, offset: tuple[int | str, int | str]
|
self, obj: Styles, offset: tuple[int | str, int | str] | ScalarOffset
|
||||||
) -> tuple[int | str, int | str]:
|
) -> tuple[int | str, int | str] | ScalarOffset:
|
||||||
obj.refresh(True)
|
obj.refresh(True)
|
||||||
|
if isinstance(offset, ScalarOffset):
|
||||||
|
setattr(obj, self._internal_name, offset)
|
||||||
|
return offset
|
||||||
x, y = offset
|
x, y = offset
|
||||||
scalar_x = (
|
scalar_x = (
|
||||||
Scalar.parse(x, Unit.WIDTH)
|
Scalar.parse(x, Unit.WIDTH)
|
||||||
|
|||||||
@@ -294,15 +294,15 @@ class StylesBuilder:
|
|||||||
style_definition = " ".join(token.value for token in tokens)
|
style_definition = " ".join(token.value for token in tokens)
|
||||||
self.styles.text_style = style_definition
|
self.styles.text_style = style_definition
|
||||||
|
|
||||||
def process_dock_group(self, name: str, tokens: list[Token]) -> None:
|
def process_dock(self, name: str, tokens: list[Token]) -> None:
|
||||||
|
|
||||||
if len(tokens) > 1:
|
if len(tokens) > 1:
|
||||||
self.error(
|
self.error(
|
||||||
name,
|
name,
|
||||||
tokens[1],
|
tokens[1],
|
||||||
f"unexpected tokens in dock-group declaration",
|
f"unexpected tokens in dock declaration",
|
||||||
)
|
)
|
||||||
self.styles._rule_dock_group = tokens[0].value if tokens else ""
|
self.styles._rule_dock = tokens[0].value if tokens else ""
|
||||||
|
|
||||||
def process_docks(self, name: str, tokens: list[Token]) -> None:
|
def process_docks(self, name: str, tokens: list[Token]) -> None:
|
||||||
docks: list[DockGroup] = []
|
docks: list[DockGroup] = []
|
||||||
@@ -333,7 +333,7 @@ class StylesBuilder:
|
|||||||
token,
|
token,
|
||||||
f"unexpected token {token.value!r} in docks declaration",
|
f"unexpected token {token.value!r} in docks declaration",
|
||||||
)
|
)
|
||||||
self.styles._rule_docks = tuple(docks)
|
self.styles._rule_docks = tuple(docks + [DockGroup("_default", "top", 0)])
|
||||||
|
|
||||||
def process_layer(self, name: str, tokens: list[Token]) -> None:
|
def process_layer(self, name: str, tokens: list[Token]) -> None:
|
||||||
if len(tokens) > 1:
|
if len(tokens) > 1:
|
||||||
|
|||||||
67
src/textual/css/scalar_animation.py
Normal file
67
src/textual/css/scalar_animation.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from .. import events
|
||||||
|
from ..geometry import Offset
|
||||||
|
from .._animator import Animation
|
||||||
|
from .scalar import ScalarOffset
|
||||||
|
from .._animator import EasingFunction
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ..widget import Widget
|
||||||
|
from .styles import Styles
|
||||||
|
|
||||||
|
|
||||||
|
class ScalarAnimation(Animation):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
widget: Widget,
|
||||||
|
styles: Styles,
|
||||||
|
start_time: float,
|
||||||
|
attribute: str,
|
||||||
|
value: ScalarOffset,
|
||||||
|
duration: float | None,
|
||||||
|
speed: float | None,
|
||||||
|
easing: EasingFunction,
|
||||||
|
):
|
||||||
|
assert (
|
||||||
|
speed is not None or duration is not None
|
||||||
|
), "One of speed or duration required"
|
||||||
|
self.widget = widget
|
||||||
|
self.styles = styles
|
||||||
|
self.start_time = start_time
|
||||||
|
self.attribute = attribute
|
||||||
|
self.final_value = value
|
||||||
|
self.easing = easing
|
||||||
|
|
||||||
|
size = widget.size
|
||||||
|
viewport = widget.app.size
|
||||||
|
|
||||||
|
self.start: Offset = getattr(styles, attribute).resolve(size, viewport)
|
||||||
|
self.destination: Offset = value.resolve(size, viewport)
|
||||||
|
|
||||||
|
if speed is not None:
|
||||||
|
distance = self.start.get_distance_to(self.destination)
|
||||||
|
self.duration = distance / speed
|
||||||
|
else:
|
||||||
|
assert duration is not None, "Duration expected to be non-None"
|
||||||
|
self.duration = duration
|
||||||
|
|
||||||
|
def __call__(self, time: float) -> bool:
|
||||||
|
|
||||||
|
factor = min(1.0, (time - self.start_time) / self.duration)
|
||||||
|
eased_factor = self.easing(factor)
|
||||||
|
|
||||||
|
if eased_factor >= 1:
|
||||||
|
offset = self.final_value
|
||||||
|
setattr(self.styles, self.attribute, self.final_value)
|
||||||
|
return True
|
||||||
|
|
||||||
|
offset = self.start + (self.destination - self.start) * eased_factor
|
||||||
|
current = getattr(self.styles, f"_rule_{self.attribute}")
|
||||||
|
if current != offset:
|
||||||
|
setattr(self.styles, f"{self.attribute}", offset)
|
||||||
|
|
||||||
|
return False
|
||||||
@@ -31,7 +31,7 @@ from ._style_properties import (
|
|||||||
BoxProperty,
|
BoxProperty,
|
||||||
ColorProperty,
|
ColorProperty,
|
||||||
DocksProperty,
|
DocksProperty,
|
||||||
DockGroupProperty,
|
DockProperty,
|
||||||
OffsetProperty,
|
OffsetProperty,
|
||||||
NameProperty,
|
NameProperty,
|
||||||
NameListProperty,
|
NameListProperty,
|
||||||
@@ -94,7 +94,7 @@ class Styles:
|
|||||||
_rule_min_width: Scalar | None = None
|
_rule_min_width: Scalar | None = None
|
||||||
_rule_min_height: Scalar | None = None
|
_rule_min_height: Scalar | None = None
|
||||||
|
|
||||||
_rule_dock_group: str | None = None
|
_rule_dock: str | None = None
|
||||||
_rule_docks: tuple[DockGroup, ...] | None = None
|
_rule_docks: tuple[DockGroup, ...] | None = None
|
||||||
|
|
||||||
_rule_layers: tuple[str, ...] | None = None
|
_rule_layers: tuple[str, ...] | None = None
|
||||||
@@ -137,7 +137,7 @@ class Styles:
|
|||||||
min_width = ScalarProperty(percent_unit=Unit.WIDTH)
|
min_width = ScalarProperty(percent_unit=Unit.WIDTH)
|
||||||
min_height = ScalarProperty(percent_unit=Unit.HEIGHT)
|
min_height = ScalarProperty(percent_unit=Unit.HEIGHT)
|
||||||
|
|
||||||
dock_group = DockGroupProperty()
|
dock = DockProperty()
|
||||||
docks = DocksProperty()
|
docks = DocksProperty()
|
||||||
|
|
||||||
layer = NameProperty()
|
layer = NameProperty()
|
||||||
@@ -145,8 +145,6 @@ class Styles:
|
|||||||
transitions = TransitionsProperty()
|
transitions = TransitionsProperty()
|
||||||
|
|
||||||
ANIMATABLE = {
|
ANIMATABLE = {
|
||||||
"offset-x",
|
|
||||||
"offset-y",
|
|
||||||
"offset",
|
"offset",
|
||||||
"padding",
|
"padding",
|
||||||
"margin",
|
"margin",
|
||||||
@@ -192,6 +190,8 @@ class Styles:
|
|||||||
def refresh(self, layout: bool = False) -> None:
|
def refresh(self, layout: bool = False) -> None:
|
||||||
self._repaint_required = True
|
self._repaint_required = True
|
||||||
self._layout_required = layout
|
self._layout_required = layout
|
||||||
|
# if self.node is not None:
|
||||||
|
# self.node.post_message_no_wait(events.Null(self.node))
|
||||||
|
|
||||||
def check_refresh(self) -> tuple[bool, bool]:
|
def check_refresh(self) -> tuple[bool, bool]:
|
||||||
result = (self._repaint_required, self._layout_required)
|
result = (self._repaint_required, self._layout_required)
|
||||||
@@ -252,25 +252,19 @@ class Styles:
|
|||||||
current = getattr(styles, f"_rule_{key}")
|
current = getattr(styles, f"_rule_{key}")
|
||||||
if current == value:
|
if current == value:
|
||||||
continue
|
continue
|
||||||
log(key, "=", value)
|
|
||||||
if is_animatable(key):
|
if is_animatable(key):
|
||||||
log("animatable", key)
|
|
||||||
transition = styles.get_transition(key)
|
transition = styles.get_transition(key)
|
||||||
log("transition", transition)
|
|
||||||
if transition is None:
|
if transition is None:
|
||||||
setattr(styles, f"_rule_{key}", value)
|
setattr(styles, f"_rule_{key}", value)
|
||||||
else:
|
else:
|
||||||
duration, easing, delay = transition
|
duration, easing, delay = transition
|
||||||
log("ANIMATING")
|
|
||||||
self.node.app.animator.animate(
|
self.node.app.animator.animate(
|
||||||
styles, key, value, duration=duration, easing=easing
|
styles, key, value, duration=duration, easing=easing
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
log("not animatable")
|
|
||||||
setattr(styles, f"_rule_{key}", value)
|
setattr(styles, f"_rule_{key}", value)
|
||||||
|
|
||||||
if self.node is not None:
|
if self.node is not None:
|
||||||
self.node.post_message_no_wait(events.Null(self.node))
|
self.node.on_style_change()
|
||||||
|
|
||||||
def __rich_repr__(self) -> rich.repr.Result:
|
def __rich_repr__(self) -> rich.repr.Result:
|
||||||
for rule_name, internal_rule_name in zip(RULE_NAMES, INTERNAL_RULE_NAMES):
|
for rule_name, internal_rule_name in zip(RULE_NAMES, INTERNAL_RULE_NAMES):
|
||||||
@@ -362,8 +356,8 @@ class Styles:
|
|||||||
if self.offset:
|
if self.offset:
|
||||||
x, y = self.offset
|
x, y = self.offset
|
||||||
append_declaration("offset", f"{x} {y}")
|
append_declaration("offset", f"{x} {y}")
|
||||||
if self._rule_dock_group:
|
if self._rule_dock:
|
||||||
append_declaration("dock-group", self._rule_dock_group)
|
append_declaration("dock-group", self._rule_dock)
|
||||||
if self._rule_docks:
|
if self._rule_docks:
|
||||||
append_declaration(
|
append_declaration(
|
||||||
"docks",
|
"docks",
|
||||||
@@ -416,7 +410,7 @@ if __name__ == "__main__":
|
|||||||
styles.outline_right = ("solid", "red")
|
styles.outline_right = ("solid", "red")
|
||||||
styles.docks = "foo bar"
|
styles.docks = "foo bar"
|
||||||
styles.text_style = "italic"
|
styles.text_style = "italic"
|
||||||
styles.dock_group = "bar"
|
styles.dock = "bar"
|
||||||
styles.layers = "foo bar"
|
styles.layers = "foo bar"
|
||||||
|
|
||||||
from rich import inspect, print
|
from rich import inspect, print
|
||||||
|
|||||||
@@ -128,7 +128,6 @@ class Stylesheet:
|
|||||||
(name, max(specificity_rules, key=get_first_item)[1])
|
(name, max(specificity_rules, key=get_first_item)[1])
|
||||||
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)
|
||||||
|
|
||||||
def update(self, root: DOMNode) -> None:
|
def update(self, root: DOMNode) -> None:
|
||||||
|
|||||||
@@ -139,9 +139,14 @@ class DOMNode(MessagePump):
|
|||||||
from .widget import Widget
|
from .widget import Widget
|
||||||
|
|
||||||
for node in self.walk_children():
|
for node in self.walk_children():
|
||||||
# node.styles = Styles()
|
node.styles = Styles(node=node)
|
||||||
if isinstance(node, Widget):
|
if isinstance(node, Widget):
|
||||||
node.clear_render_cache()
|
node.clear_render_cache()
|
||||||
|
node._repaint_required = True
|
||||||
|
node._layout_required = True
|
||||||
|
|
||||||
|
def on_style_change(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
def add_child(self, node: DOMNode) -> None:
|
def add_child(self, node: DOMNode) -> None:
|
||||||
self.children._append(node)
|
self.children._append(node)
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ class LayoutMap:
|
|||||||
|
|
||||||
layout_offset = Offset(0, 0)
|
layout_offset = Offset(0, 0)
|
||||||
if widget.styles.has_offset:
|
if widget.styles.has_offset:
|
||||||
log("r", region, "c", clip.size)
|
|
||||||
layout_offset = widget.styles.offset.resolve(region.size, clip.size)
|
layout_offset = widget.styles.offset.resolve(region.size, clip.size)
|
||||||
log("layout_offset", layout_offset)
|
|
||||||
|
|
||||||
self.widgets[widget] = RenderRegion(region + layout_offset, order, clip)
|
self.widgets[widget] = RenderRegion(region + layout_offset, order, clip)
|
||||||
|
|
||||||
@@ -70,7 +68,7 @@ class LayoutMap:
|
|||||||
self.add_widget(
|
self.add_widget(
|
||||||
sub_widget,
|
sub_widget,
|
||||||
sub_region + region.origin - scroll,
|
sub_region + region.origin - scroll,
|
||||||
sub_widget.z,
|
sub_widget.z + (z,),
|
||||||
sub_clip,
|
sub_clip,
|
||||||
)
|
)
|
||||||
view.virtual_size = total_region.size
|
view.virtual_size = total_region.size
|
||||||
|
|||||||
@@ -52,14 +52,14 @@ class DockLayout(Layout):
|
|||||||
for child in view.children:
|
for child in view.children:
|
||||||
assert isinstance(child, Widget)
|
assert isinstance(child, Widget)
|
||||||
if child.visible:
|
if child.visible:
|
||||||
groups[child.styles.dock_group].append(child)
|
groups[child.styles.dock].append(child)
|
||||||
docks: list[Dock] = []
|
docks: list[Dock] = []
|
||||||
append_dock = docks.append
|
append_dock = docks.append
|
||||||
for name, edge, z in view.styles.docks:
|
for name, edge, z in view.styles.docks:
|
||||||
append_dock(Dock(edge, groups[name], z))
|
append_dock(Dock(edge, groups[name], z))
|
||||||
return docks
|
return docks
|
||||||
|
|
||||||
def get_widgets(self, view: View) -> Iterable[DOMNode]:
|
def get_widgets(self, view: View) -> Iterable[Widget]:
|
||||||
for dock in self.get_docks(view):
|
for dock in self.get_docks(view):
|
||||||
yield from dock.widgets
|
yield from dock.widgets
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ class LayoutProperty:
|
|||||||
class View(Widget):
|
class View(Widget):
|
||||||
|
|
||||||
STYLES = """
|
STYLES = """
|
||||||
docks: main=top
|
docks: main=top;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str | None = None, id: str | None = None) -> None:
|
def __init__(self, name: str | None = None, id: str | None = None) -> None:
|
||||||
@@ -120,7 +121,6 @@ class View(Widget):
|
|||||||
if cached_size == size and cached_scroll == scroll:
|
if cached_size == size and cached_scroll == scroll:
|
||||||
return arrangement
|
return arrangement
|
||||||
arrangement = list(self._layout.arrange(self, size, scroll))
|
arrangement = list(self._layout.arrange(self, size, scroll))
|
||||||
self.log(arrangement)
|
|
||||||
self._cached_arrangement = (size, scroll, arrangement)
|
self._cached_arrangement = (size, scroll, arrangement)
|
||||||
return arrangement
|
return arrangement
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class Widget(DOMNode):
|
|||||||
can_focus: bool = False
|
can_focus: bool = False
|
||||||
|
|
||||||
STYLES = """
|
STYLES = """
|
||||||
dock-group: main;
|
dock: _default;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str | None = None, id: str | None = None) -> None:
|
def __init__(self, name: str | None = None, id: str | None = None) -> None:
|
||||||
@@ -194,6 +194,9 @@ class Widget(DOMNode):
|
|||||||
)
|
)
|
||||||
return gutter
|
return gutter
|
||||||
|
|
||||||
|
def on_style_change(self) -> None:
|
||||||
|
self.clear_render_cache()
|
||||||
|
|
||||||
def _update_size(self, size: Size) -> None:
|
def _update_size(self, size: Size) -> None:
|
||||||
self._size = size
|
self._size = size
|
||||||
|
|
||||||
@@ -286,14 +289,12 @@ class Widget(DOMNode):
|
|||||||
async def on_idle(self, event: events.Idle) -> None:
|
async def on_idle(self, event: events.Idle) -> None:
|
||||||
repaint, layout = self.styles.check_refresh()
|
repaint, layout = self.styles.check_refresh()
|
||||||
if layout or self.check_layout():
|
if layout or self.check_layout():
|
||||||
self.log("layout required")
|
# self.render_cache = None
|
||||||
self.render_cache = None
|
|
||||||
self.reset_check_repaint()
|
self.reset_check_repaint()
|
||||||
self.reset_check_layout()
|
self.reset_check_layout()
|
||||||
await self.emit(Layout(self))
|
await self.emit(Layout(self))
|
||||||
elif repaint or self.check_repaint():
|
elif repaint or self.check_repaint():
|
||||||
self.log("repaint required")
|
# self.render_cache = None
|
||||||
self.render_cache = None
|
|
||||||
self.reset_check_repaint()
|
self.reset_check_repaint()
|
||||||
await self.emit(Update(self, self))
|
await self.emit(Update(self, self))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user