203 lines
6.3 KiB
Python
203 lines
6.3 KiB
Python
import pytest
|
|
|
|
from textual.app import App
|
|
from textual.screen import Screen
|
|
from textual.widget import Widget
|
|
|
|
|
|
class Focusable(Widget, can_focus=True):
|
|
pass
|
|
|
|
|
|
class NonFocusable(Widget, can_focus=False, can_focus_children=False):
|
|
pass
|
|
|
|
|
|
class ChildrenFocusableOnly(Widget, can_focus=False, can_focus_children=True):
|
|
pass
|
|
|
|
|
|
@pytest.fixture
|
|
def screen() -> Screen:
|
|
app = App()
|
|
app._set_active()
|
|
app.push_screen(Screen())
|
|
|
|
screen = app.screen
|
|
|
|
# The classes even/odd alternate along the focus chain.
|
|
# The classes in/out identify nested widgets.
|
|
screen._add_children(
|
|
Focusable(id="foo", classes="a"),
|
|
NonFocusable(id="bar"),
|
|
Focusable(Focusable(id="Paul", classes="c"), id="container1", classes="b"),
|
|
NonFocusable(Focusable(id="Jessica", classes="a"), id="container2"),
|
|
Focusable(id="baz", classes="b"),
|
|
ChildrenFocusableOnly(Focusable(id="child", classes="c")),
|
|
)
|
|
|
|
return screen
|
|
|
|
|
|
def test_focus_chain():
|
|
app = App()
|
|
app._set_active()
|
|
app.push_screen(Screen())
|
|
|
|
screen = app.screen
|
|
|
|
# Check empty focus chain
|
|
assert not screen.focus_chain
|
|
|
|
app.screen._add_children(
|
|
Focusable(id="foo"),
|
|
NonFocusable(id="bar"),
|
|
Focusable(Focusable(id="Paul"), id="container1"),
|
|
NonFocusable(Focusable(id="Jessica"), id="container2"),
|
|
Focusable(id="baz"),
|
|
ChildrenFocusableOnly(Focusable(id="child")),
|
|
)
|
|
|
|
focus_chain = [widget.id for widget in screen.focus_chain]
|
|
assert focus_chain == ["foo", "container1", "Paul", "baz", "child"]
|
|
|
|
|
|
def test_focus_next_and_previous(screen: Screen):
|
|
assert screen.focus_next().id == "foo"
|
|
assert screen.focus_next().id == "container1"
|
|
assert screen.focus_next().id == "Paul"
|
|
assert screen.focus_next().id == "baz"
|
|
assert screen.focus_next().id == "child"
|
|
|
|
assert screen.focus_previous().id == "baz"
|
|
assert screen.focus_previous().id == "Paul"
|
|
assert screen.focus_previous().id == "container1"
|
|
assert screen.focus_previous().id == "foo"
|
|
|
|
|
|
def test_focus_next_wrap_around(screen: Screen):
|
|
"""Ensure focusing the next widget wraps around the focus chain."""
|
|
screen.set_focus(screen.query_one("#child"))
|
|
assert screen.focused.id == "child"
|
|
|
|
assert screen.focus_next().id == "foo"
|
|
|
|
|
|
def test_focus_previous_wrap_around(screen: Screen):
|
|
"""Ensure focusing the previous widget wraps around the focus chain."""
|
|
screen.set_focus(screen.query_one("#foo"))
|
|
assert screen.focused.id == "foo"
|
|
|
|
assert screen.focus_previous().id == "child"
|
|
|
|
|
|
def test_wrap_around_selector(screen: Screen):
|
|
"""Ensure moving focus in both directions wraps around the focus chain."""
|
|
screen.set_focus(screen.query_one("#foo"))
|
|
assert screen.focused.id == "foo"
|
|
|
|
assert screen.focus_previous("#Paul").id == "Paul"
|
|
assert screen.focus_next("#foo").id == "foo"
|
|
|
|
|
|
def test_no_focus_empty_selector(screen: Screen):
|
|
"""Ensure focus is cleared when selector matches nothing."""
|
|
assert screen.focus_next("#bananas") is None
|
|
assert screen.focus_previous("#bananas") is None
|
|
|
|
screen.set_focus(screen.query_one("#foo"))
|
|
assert screen.focused is not None
|
|
assert screen.focus_next("bananas") is None
|
|
assert screen.focused is None
|
|
|
|
screen.set_focus(screen.query_one("#foo"))
|
|
assert screen.focused is not None
|
|
assert screen.focus_previous("bananas") is None
|
|
assert screen.focused is None
|
|
|
|
|
|
def test_focus_next_and_previous_with_type_selector(screen: Screen):
|
|
"""Move focus with a selector that matches the currently focused node."""
|
|
screen.set_focus(screen.query_one("#Paul"))
|
|
assert screen.focused.id == "Paul"
|
|
|
|
assert screen.focus_next(Focusable).id == "baz"
|
|
assert screen.focus_next(Focusable).id == "child"
|
|
|
|
assert screen.focus_previous(Focusable).id == "baz"
|
|
assert screen.focus_previous(Focusable).id == "Paul"
|
|
assert screen.focus_previous(Focusable).id == "container1"
|
|
assert screen.focus_previous(Focusable).id == "foo"
|
|
|
|
|
|
def test_focus_next_and_previous_with_str_selector(screen: Screen):
|
|
"""Move focus with a selector that matches the currently focused node."""
|
|
screen.set_focus(screen.query_one("#foo"))
|
|
assert screen.focused.id == "foo"
|
|
|
|
assert screen.focus_next(".a").id == "foo"
|
|
assert screen.focus_next(".c").id == "Paul"
|
|
assert screen.focus_next(".c").id == "child"
|
|
|
|
assert screen.focus_previous(".c").id == "Paul"
|
|
assert screen.focus_previous(".a").id == "foo"
|
|
|
|
|
|
def test_focus_next_and_previous_with_type_selector_without_self():
|
|
"""Test moving the focus with a selector that does not match the currently focused node."""
|
|
app = App()
|
|
app._set_active()
|
|
app.push_screen(Screen())
|
|
|
|
screen = app.screen
|
|
|
|
from textual.containers import Horizontal, Vertical
|
|
from textual.widgets import Button, Input, Switch
|
|
|
|
screen._add_children(
|
|
Vertical(
|
|
Horizontal(
|
|
Input(id="w3"),
|
|
Switch(id="w4"),
|
|
Input(id="w5"),
|
|
Button(id="w6"),
|
|
Switch(id="w7"),
|
|
id="w2",
|
|
),
|
|
Horizontal(
|
|
Button(id="w9"),
|
|
Switch(id="w10"),
|
|
Button(id="w11"),
|
|
Input(id="w12"),
|
|
Input(id="w13"),
|
|
id="w8",
|
|
),
|
|
id="w1",
|
|
)
|
|
)
|
|
|
|
screen.set_focus(screen.query_one("#w3"))
|
|
assert screen.focused.id == "w3"
|
|
|
|
assert screen.focus_next(Button).id == "w6"
|
|
assert screen.focus_next(Switch).id == "w7"
|
|
assert screen.focus_next(Input).id == "w12"
|
|
|
|
assert screen.focus_previous(Button).id == "w11"
|
|
assert screen.focus_previous(Switch).id == "w10"
|
|
assert screen.focus_previous(Button).id == "w9"
|
|
assert screen.focus_previous(Input).id == "w5"
|
|
|
|
|
|
def test_focus_next_and_previous_with_str_selector_without_self(screen: Screen):
|
|
"""Test moving the focus with a selector that does not match the currently focused node."""
|
|
screen.set_focus(screen.query_one("#foo"))
|
|
assert screen.focused.id == "foo"
|
|
|
|
assert screen.focus_next(".c").id == "Paul"
|
|
assert screen.focus_next(".b").id == "baz"
|
|
assert screen.focus_next(".c").id == "child"
|
|
|
|
assert screen.focus_previous(".a").id == "foo"
|
|
assert screen.focus_previous(".a").id == "foo"
|
|
assert screen.focus_previous(".b").id == "baz"
|