CSS focus-within

This commit is contained in:
Darren Burns
2022-05-05 14:46:20 +01:00
parent e382fc14b1
commit d0ed7ef826
5 changed files with 38 additions and 6 deletions

View File

@@ -1,3 +1,7 @@
App.-show-focus *:focus {
tint: #8bc34a 50%;
}
#uber1 { #uber1 {
layout: vertical; layout: vertical;
background: green; background: green;
@@ -5,8 +9,12 @@
border: heavy white; border: heavy white;
} }
#uber1:focus-within {
background: darkslateblue;
}
.list-item { .list-item {
height: 8; height: 10;
color: #12a0; color: #12a0;
background: #ffffff00; background: #ffffff00;
} }

View File

@@ -15,15 +15,16 @@ class BasicApp(App):
self.bind("d", "dump") self.bind("d", "dump")
self.bind("t", "log_tree") self.bind("t", "log_tree")
self.bind("p", "print") self.bind("p", "print")
self.bind("o", "toggle_visibility") self.bind("v", "toggle_visibility")
self.bind("p", "toggle_display") self.bind("x", "toggle_display")
self.bind("f", "modify_focussed") self.bind("f", "modify_focussed")
self.bind("b", "toggle_border") self.bind("b", "toggle_border")
async def on_mount(self): async def on_mount(self):
"""Build layout here.""" """Build layout here."""
first_child = Placeholder(id="child1", classes="list-item")
uber1 = Widget( uber1 = Widget(
first_child,
Placeholder(id="child1", classes="list-item"), Placeholder(id="child1", classes="list-item"),
Placeholder(id="child2", classes="list-item"), Placeholder(id="child2", classes="list-item"),
Placeholder(id="child3", classes="list-item"), Placeholder(id="child3", classes="list-item"),
@@ -32,6 +33,8 @@ class BasicApp(App):
Placeholder(classes="list-item"), Placeholder(classes="list-item"),
) )
self.mount(uber1=uber1) self.mount(uber1=uber1)
self.first_child = first_child
self.uber = uber1
async def on_key(self, event: events.Key) -> None: async def on_key(self, event: events.Key) -> None:
await self.dispatch_key(event) await self.dispatch_key(event)
@@ -51,8 +54,7 @@ class BasicApp(App):
self.screen.tree, self.screen.tree,
sep=" - ", sep=" - ",
) )
print(1234, 5678) self.app.set_focus(None)
sys.stdout.write("abcdef")
def action_modify_focussed(self): def action_modify_focussed(self):
"""Increment height of focussed child, randomise border and bg color""" """Increment height of focussed child, randomise border and bg color"""

View File

@@ -536,16 +536,19 @@ class App(Generic[ReturnType], DOMNode):
# No focus, so blur currently focused widget if it exists # No focus, so blur currently focused widget if it exists
if self.focused is not None: if self.focused is not None:
self.focused.post_message_no_wait(events.Blur(self)) self.focused.post_message_no_wait(events.Blur(self))
self.focused.emit_no_wait(events.DescendantBlur(self))
self.focused = None self.focused = None
elif widget.can_focus: elif widget.can_focus:
if self.focused != widget: if self.focused != widget:
if self.focused is not None: if self.focused is not None:
# Blur currently focused widget # Blur currently focused widget
self.focused.post_message_no_wait(events.Blur(self)) self.focused.post_message_no_wait(events.Blur(self))
self.focused.emit_no_wait(events.DescendantBlur(self))
# Change focus # Change focus
self.focused = widget self.focused = widget
# Send focus event # Send focus event
widget.post_message_no_wait(events.Focus(self)) widget.post_message_no_wait(events.Focus(self))
widget.emit_no_wait(events.DescendantFocus(self))
async def _set_mouse_over(self, widget: Widget | None) -> None: async def _set_mouse_over(self, widget: Widget | None) -> None:
"""Called when the mouse is over another widget. """Called when the mouse is over another widget.

View File

@@ -393,3 +393,11 @@ class Focus(Event, bubble=False):
class Blur(Event, bubble=False): class Blur(Event, bubble=False):
pass pass
class DescendantFocus(Event, bubble=True):
pass
class DescendantBlur(Event, bubble=True):
pass

View File

@@ -98,6 +98,7 @@ class Widget(DOMNode):
auto_width = Reactive(True) auto_width = Reactive(True)
auto_height = Reactive(True) auto_height = Reactive(True)
has_focus = Reactive(False) has_focus = Reactive(False)
descendant_has_focus = Reactive(False)
mouse_over = Reactive(False) mouse_over = Reactive(False)
scroll_x = Reactive(0.0, repaint=False) scroll_x = Reactive(0.0, repaint=False)
scroll_y = Reactive(0.0, repaint=False) scroll_y = Reactive(0.0, repaint=False)
@@ -424,6 +425,8 @@ class Widget(DOMNode):
yield "hover" yield "hover"
if self.has_focus: if self.has_focus:
yield "focus" yield "focus"
if self.descendant_has_focus:
yield "focus-within"
def watch(self, attribute_name, callback: Callable[[Any], Awaitable[None]]) -> None: def watch(self, attribute_name, callback: Callable[[Any], Awaitable[None]]) -> None:
watch(self, attribute_name, callback) watch(self, attribute_name, callback)
@@ -721,11 +724,19 @@ class Widget(DOMNode):
self.mouse_over = True self.mouse_over = True
def on_focus(self, event: events.Focus) -> None: def on_focus(self, event: events.Focus) -> None:
self.emit_no_wait(events.DescendantFocus(self))
self.has_focus = True self.has_focus = True
def on_blur(self, event: events.Blur) -> None: def on_blur(self, event: events.Blur) -> None:
self.emit_no_wait(events.DescendantBlur(self))
self.has_focus = False self.has_focus = False
def on_descendant_focus(self, event: events.DescendantFocus) -> None:
self.descendant_has_focus = True
def on_descendant_blur(self, event: events.DescendantBlur) -> None:
self.descendant_has_focus = False
def on_mouse_scroll_down(self, event) -> None: def on_mouse_scroll_down(self, event) -> None:
if self.is_container: if self.is_container:
self.scroll_down(animate=False) self.scroll_down(animate=False)