diff --git a/CHANGELOG.md b/CHANGELOG.md index 649b6c1901..35fd914feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,14 @@ These changes are available on the `master` branch, but have not yet been releas ### Fixed +- Fixed the `view` attribute on many view items being incorrect. + ([#2981](https://github.com/Pycord-Development/pycord/pull/2981)) + ### Deprecated +- Deprecated manually setting the `view` attribute on view items. + ([#2981](https://github.com/Pycord-Development/pycord/pull/2981)) + ### Removed ## [2.7.0rc2] - 2025-10-22 diff --git a/discord/ui/action_row.py b/discord/ui/action_row.py index d3607867b7..6737a1625b 100644 --- a/discord/ui/action_row.py +++ b/discord/ui/action_row.py @@ -140,7 +140,6 @@ def add_item(self, item: ViewItem) -> Self: if self.width + item.width > 5: raise ValueError(f"Not enough space left on this ActionRow") - item._view = self.view item.parent = self self.children.append(item) @@ -162,6 +161,7 @@ def remove_item(self, item: ViewItem | str | int) -> Self: self.children.remove(item) except ValueError: pass + item.parent = None return self def get_item(self, id: str | int) -> ViewItem | None: @@ -351,13 +351,6 @@ def add_select( return self.add_item(select) - @ViewItem.view.setter - def view(self, value): - self._view = value - for item in self.children: - item.parent = self - item._view = value - def is_dispatchable(self) -> bool: return any(item.is_dispatchable() for item in self.children) diff --git a/discord/ui/container.py b/discord/ui/container.py index f676f4b086..4c738c8fdf 100644 --- a/discord/ui/container.py +++ b/discord/ui/container.py @@ -149,9 +149,6 @@ def add_item(self, item: ViewItem) -> Self: f"{item.__class__!r} cannot be added directly. Use ActionRow instead." ) - item._view = self.view - if hasattr(item, "items"): - item.view = self item.parent = self self.items.append(item) @@ -170,12 +167,13 @@ def remove_item(self, item: ViewItem | str | int) -> Self: if isinstance(item, (str, int)): item = self.get_item(item) try: - if isinstance(item, Container): + if item.parent is self: self.items.remove(item) else: item.parent.remove_item(item) except ValueError: pass + item.parent = None return self def get_item(self, id: str | int) -> ViewItem | None: @@ -361,15 +359,6 @@ def colour(self, value: int | Colour | None): # type: ignore color = colour - @ViewItem.view.setter - def view(self, value): - self._view = value - for item in self.items: - item.parent = self - item._view = value - if hasattr(item, "items") or hasattr(item, "children"): - item.view = value - def is_dispatchable(self) -> bool: return any(item.is_dispatchable() for item in self.items) diff --git a/discord/ui/item.py b/discord/ui/item.py index 958f60a7a7..8f86566241 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -35,6 +35,8 @@ "ModalItem", ) +from ..utils import warn_deprecated + if TYPE_CHECKING: from ..components import Component from ..enums import ComponentType @@ -152,7 +154,7 @@ def __init__(self): self._view: V | None = None self._row: int | None = None self._rendered_row: int | None = None - self.parent: ViewItem | BaseView | None = self.view + self.parent: ViewItem | BaseView | None = None @property def row(self) -> int | None: @@ -208,10 +210,19 @@ def view(self) -> V | None: Optional[:class:`BaseView`] The parent view of this item, or ``None`` if the item is not attached to any view. """ - return self._view + if self._view: + return self._view + if self.parent: + from .view import BaseView + + if isinstance(self.parent, BaseView): + return self.parent + return self.parent.view + return None @view.setter - def view(self, value) -> None: + def view(self, value: V | None) -> None: + warn_deprecated("Manually setting .view", since="2.7", removed="2.8") self._view = value async def callback(self, interaction: Interaction): diff --git a/discord/ui/section.py b/discord/ui/section.py index 79d092595c..069b600ea5 100644 --- a/discord/ui/section.py +++ b/discord/ui/section.py @@ -162,6 +162,7 @@ def remove_item(self, item: ViewItem | str | int) -> Self: self.items.remove(item) except ValueError: pass + item.parent = None return self def get_item(self, id: int | str) -> ViewItem | None: @@ -226,8 +227,7 @@ def set_accessory(self, item: ViewItem) -> Self: if not isinstance(item, ViewItem): raise TypeError(f"expected ViewItem not {item.__class__!r}") - if self.view: - item._view = self.view + item.parent = self self.accessory = item @@ -260,13 +260,6 @@ def set_thumbnail( return self.set_accessory(thumbnail) - @ViewItem.view.setter - def view(self, value): - self._view = value - for item in self.walk_items(): - item._view = value - item.parent = self - def copy_text(self) -> str: """Returns the text of all :class:`~discord.ui.TextDisplay` items in this section. Equivalent to the `Copy Text` option on Discord clients. diff --git a/discord/ui/view.py b/discord/ui/view.py index 9d03339d09..f71cbdd692 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -235,11 +235,10 @@ def add_item(self, item: ViewItem[V]) -> Self: raise TypeError(f"expected ViewItem not {item.__class__!r}") item.parent = self - item._view = self self.children.append(item) return self - def remove_item(self, item: ViewItem[V] | int | str) -> None: + def remove_item(self, item: ViewItem[V] | int | str) -> Self: """Removes an item from the view. If an :class:`int` or :class:`str` is passed, the item will be removed by ViewItem ``id`` or ``custom_id`` respectively. @@ -252,16 +251,19 @@ def remove_item(self, item: ViewItem[V] | int | str) -> None: if isinstance(item, (str, int)): item = self.get_item(item) try: - if isinstance(item.parent, BaseView): + if item.parent is self: self.children.remove(item) else: item.parent.remove_item(item) except ValueError: pass + item.parent = None return self - def clear_items(self) -> None: + def clear_items(self) -> Self: """Removes all items from this view.""" + for child in self.children: + child.parent = None self.children.clear() return self @@ -575,7 +577,6 @@ def __init__( **func.__discord_ui_model_kwargs__ ) item.callback = partial(func, self, item) - item._view = self item.parent = self setattr(self, func.__name__, item) self.children.append(item) @@ -882,8 +883,6 @@ def add_item(self, item: ViewItem[V]) -> Self: ) super().add_item(item) - if hasattr(item, "items"): - item.view = self return self def refresh(self, components: list[Component]):