divide optimizations

This commit is contained in:
Will McGugan
2022-07-11 12:45:54 +01:00
parent d7d1b6b197
commit def1f5d6ab
8 changed files with 51 additions and 26 deletions

27
poetry.lock generated
View File

@@ -500,7 +500,7 @@ testing = ["pytest", "pytest-benchmark"]
[[package]] [[package]]
name = "pre-commit" name = "pre-commit"
version = "2.19.0" version = "2.20.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks." description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev" category = "dev"
optional = false optional = false
@@ -658,16 +658,21 @@ version = "12.4.4"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6.3,<4.0.0" python-versions = "^3.6.3"
develop = true
[package.dependencies] [package.dependencies]
commonmark = ">=0.9.0,<0.10.0" commonmark = "^0.9.0"
pygments = ">=2.6.0,<3.0.0" pygments = "^2.6.0"
typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} typing-extensions = {version = ">=4.0.0, <5.0", markers = "python_version < \"3.9\""}
[package.extras] [package.extras]
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
[package.source]
type = "directory"
url = "../rich"
[[package]] [[package]]
name = "six" name = "six"
version = "1.16.0" version = "1.16.0"
@@ -780,7 +785,7 @@ dev = ["aiohttp", "click", "msgpack"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "8ce8d66466dad1b984673595ebd0cc7bc0d28c7a672269e9b5620c242d87d9ad" content-hash = "08c432accbe56db11ca9b2d69c2b4e15967e1a0142d4508f245715cae6f3d239"
[metadata.files] [metadata.files]
aiohttp = [ aiohttp = [
@@ -1295,10 +1300,7 @@ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
] ]
pre-commit = [ pre-commit = []
{file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"},
{file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"},
]
py = [ py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
@@ -1375,10 +1377,7 @@ pyyaml-env-tag = [
{file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
{file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
] ]
rich = [ rich = []
{file = "rich-12.4.4-py3-none-any.whl", hash = "sha256:d2bbd99c320a2532ac71ff6a3164867884357da3e3301f0240090c5d2fdac7ec"},
{file = "rich-12.4.4.tar.gz", hash = "sha256:4c586de507202505346f3e32d1363eb9ed6932f0c2f63184dea88983ff4971e2"},
]
six = [ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},

View File

@@ -22,8 +22,8 @@ textual = "textual.cli.cli:run"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.7" python = "^3.7"
rich = "^12.4.3" #rich = "^12.4.3"
#rich = {path="../rich", develop=true} rich = {path="../rich", develop=true}
importlib-metadata = "^4.11.3" importlib-metadata = "^4.11.3"
typing-extensions = { version = "^4.0.0", python = "<3.8" } typing-extensions = { version = "^4.0.0", python = "<3.8" }

View File

@@ -594,7 +594,6 @@ class Compositor:
] ]
return segment_lines return segment_lines
@timer("render")
def render(self, full: bool = False) -> RenderableType | None: def render(self, full: bool = False) -> RenderableType | None:
"""Render a layout. """Render a layout.

View File

@@ -645,6 +645,7 @@ class App(Generic[ReturnType], DOMNode):
# Change focus # Change focus
self.focused = widget self.focused = widget
# Send focus event # Send focus event
widget.parent.scroll_to_widget(widget)
widget.post_message_no_wait(events.Focus(self)) widget.post_message_no_wait(events.Focus(self))
widget.emit_no_wait(events.DescendantFocus(self)) widget.emit_no_wait(events.DescendantFocus(self))
@@ -926,7 +927,6 @@ class App(Generic[ReturnType], DOMNode):
stylesheet.update(self.app, animate=animate) stylesheet.update(self.app, animate=animate)
self.screen._refresh_layout(self.size, full=True) self.screen._refresh_layout(self.size, full=True)
@timer("_display")
def _display(self, renderable: RenderableType | None) -> None: def _display(self, renderable: RenderableType | None) -> None:
"""Display a renderable within a sync. """Display a renderable within a sync.

View File

@@ -569,8 +569,27 @@ class Region(NamedTuple):
) )
return new_region return new_region
def grow(self, margin: tuple[int, int, int, int]) -> Region:
"""Grow a region by adding spacing.
Args:
margin (Spacing): Defines how many cells to grow the Region by at each edge.
Returns:
Region: New region.
"""
top, right, bottom, left = margin
x, y, width, height = self
return Region(
x=x - left,
y=y - top,
width=max(0, width + left + right),
height=max(0, height + top + bottom),
)
def shrink(self, margin: tuple[int, int, int, int]) -> Region: def shrink(self, margin: tuple[int, int, int, int]) -> Region:
"""Shrink a region by pushing each edge inwards. """Shrink a region by subtracting spacing.
Args: Args:
margin (Spacing): Defines how many cells to shrink the Region by at each edge. margin (Spacing): Defines how many cells to shrink the Region by at each edge.

View File

@@ -688,9 +688,7 @@ class Widget(DOMNode):
) )
def scroll_to_widget(self, widget: Widget, *, animate: bool = True) -> bool: def scroll_to_widget(self, widget: Widget, *, animate: bool = True) -> bool:
"""Starting from `widget`, travel up the DOM to this node, scrolling all containers such that """Scroll scrolling to bring a widget in to view.
every widget is visible within its parent container. This will, in the majority of cases,
bring the target widget into
Args: Args:
widget (Widget): A descendant widget. widget (Widget): A descendant widget.
@@ -705,7 +703,8 @@ class Widget(DOMNode):
node = widget.parent node = widget.parent
child = widget child = widget
while node: while isinstance(node, Widget):
assert isinstance(child, Widget)
try: try:
widget_region = child.region widget_region = child.region
container_region = node.region container_region = node.region
@@ -718,12 +717,14 @@ class Widget(DOMNode):
node = node.parent node = node.parent
continue continue
widget_region = widget_region.grow(widget.styles.margin)
# We can either scroll so the widget is at the top of the container, or so that # We can either scroll so the widget is at the top of the container, or so that
# it is at the bottom. We want to pick which has the shortest distance # it is at the bottom. We want to pick which has the shortest distance
top_delta = widget_region.offset - container_region.origin top_delta = widget_region.offset - container_region.offset
bottom_delta = widget_region.offset - ( bottom_delta = widget_region.offset - (
container_region.origin container_region.offset
+ Offset(0, container_region.height - widget_region.height) + Offset(0, container_region.height - widget_region.height)
) )

View File

@@ -43,7 +43,7 @@ class Button(Widget, can_focus=True):
} }
Button:focus { Button:focus {
text-style: bold underline; text-style: bold reverse;
} }
Button:hover { Button:hover {
@@ -183,6 +183,7 @@ class Button(Widget, can_focus=True):
def render(self) -> RenderableType: def render(self) -> RenderableType:
label = self.label.copy() label = self.label.copy()
label = Text.assemble(" ", label, " ")
label.stylize(self.text_style) label.stylize(self.text_style)
return label return label

View File

@@ -251,6 +251,12 @@ def test_region_shrink():
assert region.shrink(margin) == Region(x=14, y=11, width=44, height=46) assert region.shrink(margin) == Region(x=14, y=11, width=44, height=46)
def test_region_grow():
margin = Spacing(top=1, right=2, bottom=3, left=4)
region = Region(x=10, y=10, width=50, height=50)
assert region.grow(margin) == Region(x=6, y=9, width=56, height=54)
def test_region_intersection(): def test_region_intersection():
assert Region(0, 0, 100, 50).intersection(Region(10, 10, 10, 10)) == Region( assert Region(0, 0, 100, 50).intersection(Region(10, 10, 10, 10)) == Region(
10, 10, 10, 10 10, 10, 10, 10