From ac4c3120b113552b7cbe332099a873ba67c83f60 Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Mon, 2 Dec 2024 03:04:27 +0400 Subject: [PATCH] fix: closing an application now works even if the application is not the root of its hierarchy --- CHANGELOG.md | 4 ++++ ubo_gui/menu/menu_widget.py | 37 ++++++++++++++++++------------------- ubo_gui/menu/stack_item.py | 16 ++++++++++++---- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 520dfea..5b1cad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Upcoming + +- fix: closing an application now works even if the application is not the root of its hierarchy + ## Version 0.13.9 - refactor: set parent of `StackApplicationItem`s like `StackMenuItem`s diff --git a/ubo_gui/menu/menu_widget.py b/ubo_gui/menu/menu_widget.py index 179ac72..52c45e1 100644 --- a/ubo_gui/menu/menu_widget.py +++ b/ubo_gui/menu/menu_widget.py @@ -601,30 +601,29 @@ def close_application(self: MenuWidget, application: PageWidget) -> None: # If any of these applications are the top of the stack, remove it with `pop` to # ensure the animation is played. with self.stack_lock: - if any( - isinstance(item.root, StackApplicationItem) - and item.root.application is application + to_be_removed = [ + cast(StackApplicationItem, item) for item in self.stack - ): - to_be_removed = [ - cast(StackApplicationItem, item) - for item in self.stack - if isinstance(item.root, StackApplicationItem) - and item.root.application is application - and item is not self.top - ] - - for item in to_be_removed: + if any( + isinstance(item, StackApplicationItem) + and item.application is application + for item in item.lineage + ) + ] + + for item in to_be_removed: + if item is not self.top: item.clear_subscriptions() item.application.dispatch('on_close') - self.stack = [item for item in self.stack if item not in to_be_removed] + self.stack = [ + item + for item in self.stack + if item not in to_be_removed or item is self.top + ] - if ( - isinstance(self.top.root, StackApplicationItem) - and self.top.root.application is application - ): - self._pop() + if self.top in to_be_removed: + self._pop() @property def root(self: MenuWidget) -> StackMenuItem: diff --git a/ubo_gui/menu/stack_item.py b/ubo_gui/menu/stack_item.py index 95a83c9..53504ed 100644 --- a/ubo_gui/menu/stack_item.py +++ b/ubo_gui/menu/stack_item.py @@ -2,9 +2,9 @@ from __future__ import annotations -from collections.abc import Sequence +from collections.abc import Generator, Sequence from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Self, TypeVar, cast +from typing import TYPE_CHECKING, Self, TypeVar if TYPE_CHECKING: from collections.abc import Callable @@ -31,11 +31,19 @@ def clear_subscriptions(self: BaseStackItem) -> None: unsubscribe() @property - def root(self: BaseStackItem) -> StackMenuItem: + def root(self: BaseStackItem) -> BaseStackItem: """Return the root item.""" if self.parent: return self.parent.root - return cast(StackMenuItem, self) + return self + + @property + def lineage(self: BaseStackItem) -> Generator[BaseStackItem, None, None]: + """A generator iterating from the current item to its root.""" + current = self + while current: + yield current + current = current.parent @property def title(self: Self) -> str: