from __future__ import annotations import abc import typing import warnings if typing.TYPE_CHECKING: from collections.abc import Iterator from .widget import Widget class WidgetContainerMixin: """ Mixin class for widget containers implementing common container methods """ def __getitem__(self, position) -> Widget: """ Container short-cut for self.contents[position][0].base_widget which means "give me the child widget at position without any widget decorations". This allows for concise traversal of nested container widgets such as: my_widget[position0][position1][position2] ... """ return self.contents[position][0].base_widget def get_focus_path(self): """ Return the .focus_position values starting from this container and proceeding along each child widget until reaching a leaf (non-container) widget. """ out = [] w = self while True: try: p = w.focus_position except IndexError: return out out.append(p) w = w.focus.base_widget def set_focus_path(self, positions): """ Set the .focus_position property starting from this container widget and proceeding along newly focused child widgets. Any failed assignment due do incompatible position types or invalid positions will raise an IndexError. This method may be used to restore a particular widget to the focus by passing in the value returned from an earlier call to get_focus_path(). positions -- sequence of positions """ w = self for p in positions: if p != w.focus_position: w.focus_position = p # modifies w.focus w = w.focus.base_widget def get_focus_widgets(self) -> list[Widget]: """ Return the .focus values starting from this container and proceeding along each child widget until reaching a leaf (non-container) widget. Note that the list does not contain the topmost container widget (i.e., on which this method is called), but does include the lowest leaf widget. """ out = [] w = self while True: w = w.base_widget.focus if w is None: return out out.append(w) @property @abc.abstractmethod def focus(self) -> Widget: """ Read-only property returning the child widget in focus for container widgets. This default implementation always returns ``None``, indicating that this widget has no children. """ def _get_focus(self) -> Widget: warnings.warn( f"method `{self.__class__.__name__}._get_focus` is deprecated, " f"please use `{self.__class__.__name__}.focus` property", DeprecationWarning, stacklevel=3, ) return self.focus class WidgetContainerListContentsMixin: """ Mixin class for widget containers whose positions are indexes into a list available as self.contents. """ def __iter__(self) -> Iterator[int]: """ Return an iterable of positions for this container from first to last. """ return iter(range(len(self.contents))) def __reversed__(self) -> Iterator[int]: """ Return an iterable of positions for this container from last to first. """ return iter(range(len(self.contents) - 1, -1, -1)) def __len__(self) -> int: return len(self.contents) @property @abc.abstractmethod def contents(self) -> list[tuple[Widget, typing.Any]]: """The contents of container as a list of (widget, options)""" @contents.setter def contents(self, new_contents: list[tuple[Widget, typing.Any]]) -> None: """The contents of container as a list of (widget, options)""" def _get_contents(self) -> list[tuple[Widget, typing.Any]]: warnings.warn( f"method `{self.__class__.__name__}._get_contents` is deprecated, " f"please use `{self.__class__.__name__}.contents` property", DeprecationWarning, stacklevel=2, ) return self.contents def _set_contents(self, c: list[tuple[Widget, typing.Any]]) -> None: warnings.warn( f"method `{self.__class__.__name__}._set_contents` is deprecated, " f"please use `{self.__class__.__name__}.contents` property", DeprecationWarning, stacklevel=2, ) self.contents = c @property @abc.abstractmethod def focus_position(self) -> int | None: """ index of child widget in focus. """ @focus_position.setter def focus_position(self, position: int) -> None: """ index of child widget in focus. """ def _get_focus_position(self) -> int | None: warnings.warn( f"method `{self.__class__.__name__}._get_focus_position` is deprecated, " f"please use `{self.__class__.__name__}.focus_position` property", DeprecationWarning, stacklevel=3, ) return self.focus_position def _set_focus_position(self, position: int) -> None: """ Set the widget in focus. position -- index of child widget to be made focus """ warnings.warn( f"method `{self.__class__.__name__}._set_focus_position` is deprecated, " f"please use `{self.__class__.__name__}.focus_position` property", DeprecationWarning, stacklevel=3, ) self.focus_position = position