mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'main' into worker-coverage
This commit is contained in:
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Fixed zero division error https://github.com/Textualize/textual/issues/2673
|
||||
- Fix `scroll_to_center` when there were nested layers out of view (Compositor full_map not populated fully) https://github.com/Textualize/textual/pull/2684
|
||||
- Issue with computing progress in workers https://github.com/Textualize/textual/pull/2686
|
||||
- Issues with `switch_screen` not updating the results callback appropriately https://github.com/Textualize/textual/issues/2650
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
@@ -23,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- `SuggestFromList` class to let widgets get completions from a fixed set of options https://github.com/Textualize/textual/pull/2604
|
||||
- `Input` has a new component class `input--suggestion` https://github.com/Textualize/textual/pull/2604
|
||||
- Added `Widget.remove_children` https://github.com/Textualize/textual/pull/2657
|
||||
- Added `Validator` framework and validation for `Input` https://github.com/Textualize/textual/pull/2600
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -36,6 +39,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- `Tree` and `DirectoryTree` Messages no longer accept a `tree` parameter, using `self.node.tree` instead. https://github.com/Textualize/textual/issues/2529
|
||||
- Keybinding <kbd>right</kbd> in `Input` is also used to accept a suggestion if the cursor is at the end of the input https://github.com/Textualize/textual/pull/2604
|
||||
- `Input.__init__` now accepts a `suggester` attribute for completion suggestions https://github.com/Textualize/textual/pull/2604
|
||||
- Using `switch_screen` to switch to the currently active screen is now a no-op https://github.com/Textualize/textual/pull/2692
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
@@ -1611,15 +1611,19 @@ class App(Generic[ReturnType], DOMNode):
|
||||
raise TypeError(
|
||||
f"switch_screen requires a Screen instance or str; not {screen!r}"
|
||||
)
|
||||
if self.screen is not screen:
|
||||
previous_screen = self._replace_screen(self._screen_stack.pop())
|
||||
previous_screen._pop_result_callback()
|
||||
next_screen, await_mount = self._get_screen(screen)
|
||||
self._screen_stack.append(next_screen)
|
||||
self.screen.post_message(events.ScreenResume())
|
||||
self.log.system(f"{self.screen} is current (SWITCHED)")
|
||||
return await_mount
|
||||
return AwaitMount(self.screen, [])
|
||||
|
||||
next_screen, await_mount = self._get_screen(screen)
|
||||
if screen is self.screen or next_screen is self.screen:
|
||||
self.log.system(f"Screen {screen} is already current.")
|
||||
return AwaitMount(self.screen, [])
|
||||
|
||||
previous_screen = self._replace_screen(self._screen_stack.pop())
|
||||
previous_screen._pop_result_callback()
|
||||
self._screen_stack.append(next_screen)
|
||||
self.screen.post_message(events.ScreenResume())
|
||||
self.screen._push_result_callback(self.screen, None)
|
||||
self.log.system(f"{self.screen} is current (SWITCHED)")
|
||||
return await_mount
|
||||
|
||||
def install_screen(self, screen: Screen, name: str) -> None:
|
||||
"""Install a screen.
|
||||
|
||||
@@ -90,7 +90,7 @@ class GenericProperty(Generic[PropertyGetType, PropertySetType]):
|
||||
# Raise StyleValueError here
|
||||
return cast(PropertyGetType, value)
|
||||
|
||||
def __set_name__(self, owner: Styles, name: str) -> None:
|
||||
def __set_name__(self, owner: StylesBase, name: str) -> None:
|
||||
self.name = name
|
||||
|
||||
def __get__(
|
||||
@@ -138,7 +138,7 @@ class ScalarProperty:
|
||||
self.allow_auto = allow_auto
|
||||
super().__init__()
|
||||
|
||||
def __set_name__(self, owner: Styles, name: str) -> None:
|
||||
def __set_name__(self, owner: StylesBase, name: str) -> None:
|
||||
self.name = name
|
||||
|
||||
def __get__(
|
||||
@@ -227,7 +227,7 @@ class ScalarListProperty:
|
||||
self.percent_unit = percent_unit
|
||||
self.refresh_children = refresh_children
|
||||
|
||||
def __set_name__(self, owner: Styles, name: str) -> None:
|
||||
def __set_name__(self, owner: StylesBase, name: str) -> None:
|
||||
self.name = name
|
||||
|
||||
def __get__(
|
||||
@@ -294,7 +294,7 @@ class BoxProperty:
|
||||
obj.get_rule(self.name) or ("", self._default_color),
|
||||
)
|
||||
|
||||
def __set__(self, obj: Styles, border: tuple[EdgeType, str | Color] | None):
|
||||
def __set__(self, obj: StylesBase, border: tuple[EdgeType, str | Color] | None):
|
||||
"""Set the box property.
|
||||
|
||||
Args:
|
||||
@@ -928,7 +928,7 @@ class ColorProperty:
|
||||
class StyleFlagsProperty:
|
||||
"""Descriptor for getting and set style flag properties (e.g. ``bold italic underline``)."""
|
||||
|
||||
def __set_name__(self, owner: Styles, name: str) -> None:
|
||||
def __set_name__(self, owner: StylesBase, name: str) -> None:
|
||||
self.name = name
|
||||
|
||||
def __get__(
|
||||
@@ -1008,7 +1008,9 @@ class TransitionsProperty:
|
||||
"""
|
||||
return cast("dict[str, Transition]", obj.get_rule("transitions", {}))
|
||||
|
||||
def __set__(self, obj: Styles, transitions: dict[str, Transition] | None) -> None:
|
||||
def __set__(
|
||||
self, obj: StylesBase, transitions: dict[str, Transition] | None
|
||||
) -> None:
|
||||
_rich_traceback_omit = True
|
||||
if transitions is None:
|
||||
obj.clear_rule("transitions")
|
||||
|
||||
@@ -298,3 +298,55 @@ async def test_dismiss_non_top_screen():
|
||||
await pilot.press("p")
|
||||
with pytest.raises(ScreenStackError):
|
||||
app.bottom.dismiss()
|
||||
|
||||
|
||||
async def test_switch_screen_no_op():
|
||||
"""Regression test for https://github.com/Textualize/textual/issues/2650"""
|
||||
|
||||
class MyScreen(Screen):
|
||||
pass
|
||||
|
||||
class MyApp(App[None]):
|
||||
SCREENS = {"screen": MyScreen()}
|
||||
|
||||
def on_mount(self):
|
||||
self.push_screen("screen")
|
||||
|
||||
app = MyApp()
|
||||
async with app.run_test():
|
||||
screen_id = id(app.screen)
|
||||
app.switch_screen("screen")
|
||||
assert screen_id == id(app.screen)
|
||||
app.switch_screen("screen")
|
||||
assert screen_id == id(app.screen)
|
||||
|
||||
|
||||
async def test_switch_screen_updates_results_callback_stack():
|
||||
"""Regression test for https://github.com/Textualize/textual/issues/2650"""
|
||||
|
||||
class ScreenA(Screen):
|
||||
pass
|
||||
|
||||
class ScreenB(Screen):
|
||||
pass
|
||||
|
||||
class MyApp(App[None]):
|
||||
SCREENS = {
|
||||
"a": ScreenA(),
|
||||
"b": ScreenB(),
|
||||
}
|
||||
|
||||
def callback(self, _):
|
||||
return 42
|
||||
|
||||
def on_mount(self):
|
||||
self.push_screen("a", self.callback)
|
||||
|
||||
app = MyApp()
|
||||
async with app.run_test():
|
||||
assert len(app.screen._result_callbacks) == 1
|
||||
assert app.screen._result_callbacks[-1].callback(None) == 42
|
||||
|
||||
app.switch_screen("b")
|
||||
assert len(app.screen._result_callbacks) == 1
|
||||
assert app.screen._result_callbacks[-1].callback is None
|
||||
|
||||
Reference in New Issue
Block a user