fix for focus reset edge case

This commit is contained in:
Will McGugan
2022-10-20 19:36:36 +01:00
parent af5a257660
commit e419a0821c
5 changed files with 44 additions and 12 deletions

View File

@@ -196,6 +196,7 @@ DataTable {
height: 16;
}
LoginForm {
height: auto;
margin: 1 0;

View File

@@ -1,5 +1,7 @@
from __future__ import annotations
from pathlib import Path
from rich import box
from rich.console import RenderableType
from rich.markdown import Markdown
@@ -303,6 +305,7 @@ class DemoApp(App):
self.query_one(TextLog).write(renderable)
def compose(self) -> ComposeResult:
example_css = "\n".join(Path(self.css_path).read_text().splitlines()[:50])
yield Container(
Sidebar(classes="-hidden"),
Header(show_clock=True),
@@ -341,9 +344,15 @@ class DemoApp(App):
Section(
SectionTitle("CSS"),
Text(Markdown(CSS_MD)),
Static(
Syntax(
EXAMPLE_CSS, "css", theme="material", line_numbers=True
Window(
Static(
Syntax(
example_css,
"css",
theme="material",
line_numbers=True,
),
expand=True,
)
),
),

View File

@@ -278,7 +278,7 @@ class DOMNode(MessagePump):
while node and not isinstance(node, Screen):
node = node._parent
if not isinstance(node, Screen):
raise NoScreen(f"{self} has no screen")
raise NoScreen(f"node has no screen")
return node
@property

View File

@@ -225,7 +225,7 @@ class Screen(Widget):
return self._move_focus(-1)
def _reset_focus(
self, widget: Widget, avoiding: list[DOMNode] | None = None
self, widget: Widget, avoiding: list[Widget] | None = None
) -> None:
"""Reset the focus when a widget is removed
@@ -252,14 +252,20 @@ class Screen(Widget):
# the focus chain.
widget_index = focusable_widgets.index(widget)
except ValueError:
# Seems we can't find it. There's no good reason this should
# happen but, on the off-chance, let's go into a "no focus" state.
self.set_focus(None)
# widget is not in focusable widgets
# It may have been made invisible
# Move to a sibling if possible
for sibling in widget.visible_siblings:
if sibling not in avoiding and sibling.can_focus:
self.set_focus(sibling)
break
else:
self.set_focus(None)
return
# Now go looking for something before it, that isn't about to be
# removed, and which can receive focus, and go focus that.
chosen: DOMNode | None = None
chosen: Widget | None = None
for candidate in reversed(
focusable_widgets[widget_index + 1 :] + focusable_widgets[:widget_index]
):
@@ -368,6 +374,7 @@ class Screen(Widget):
hidden, shown, resized = self._compositor.reflow(self, size)
Hide = events.Hide
Show = events.Show
for widget in hidden:
widget.post_message_no_wait(Hide(self))
for widget in shown:

View File

@@ -261,6 +261,18 @@ class Widget(DOMNode):
else:
return []
@property
def visible_siblings(self) -> list[Widget]:
"""A list of siblings which will be shown.
Returns:
list[Widget]: List of siblings.
"""
siblings = [
widget for widget in self.siblings if widget.visible and widget.display
]
return siblings
@property
def allow_vertical_scroll(self) -> bool:
"""Check if vertical scroll is permitted.
@@ -1576,9 +1588,12 @@ class Widget(DOMNode):
yield "hover"
if self.has_focus:
yield "focus"
focused = self.screen.focused
if focused and self in focused.ancestors:
yield "focus-within"
try:
focused = self.screen.focused
if focused and self in focused.ancestors:
yield "focus-within"
except NoScreen:
pass
def post_render(self, renderable: RenderableType) -> ConsoleRenderable:
"""Applies style attributes to the default renderable.