From 9ef5b054e5aa910bc1577d9827873c338c91dce8 Mon Sep 17 00:00:00 2001 From: Rapptz Date: Mon, 18 Aug 2025 04:49:20 -0400 Subject: [PATCH] Refactor total children count to an overrideable method --- discord/ui/action_row.py | 17 +++++++++-------- discord/ui/container.py | 22 ++++++++++++---------- discord/ui/section.py | 17 +++++++++-------- discord/ui/view.py | 20 +++++++++++--------- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/discord/ui/action_row.py b/discord/ui/action_row.py index 7d2fc64a9..dbe455462 100644 --- a/discord/ui/action_row.py +++ b/discord/ui/action_row.py @@ -234,7 +234,8 @@ class ActionRow(Item[V]): TypeError An :class:`Item` was not passed. ValueError - Maximum number of children has been exceeded (5). + Maximum number of children has been exceeded (5) + or (40) for the entire view. """ if (self._weight + item.width) > 5: @@ -246,14 +247,14 @@ class ActionRow(Item[V]): if not isinstance(item, Item): raise TypeError(f'expected Item not {item.__class__.__name__}') + if self._view: + self._view._add_count(1) + item._update_view(self.view) item._parent = self self._weight += 1 self._children.append(item) - if self._view: - self._view._total_children += 1 - return self def remove_item(self, item: Item[Any]) -> Self: @@ -273,8 +274,8 @@ class ActionRow(Item[V]): except ValueError: pass else: - if self._view and self._view._is_layout(): - self._view._total_children -= 1 + if self._view: + self._view._add_count(-1) self._weight -= 1 return self @@ -305,8 +306,8 @@ class ActionRow(Item[V]): This function returns the class instance to allow for fluent-style chaining. """ - if self._view and self._view._is_layout(): - self._view._total_children -= len(self._children) + if self._view: + self._view._add_count(-len(self._children)) self._children.clear() self._weight = 0 return self diff --git a/discord/ui/container.py b/discord/ui/container.py index bbd923989..8e9130320 100644 --- a/discord/ui/container.py +++ b/discord/ui/container.py @@ -296,18 +296,20 @@ class Container(Item[V]): ------ TypeError An :class:`Item` was not passed. + ValueError + Maximum number of children has been exceeded (40) for the entire view. """ if not isinstance(item, Item): raise TypeError(f'expected Item not {item.__class__.__name__}') + if item._has_children() and self._view: + self._view._add_count(len(tuple(item.walk_children()))) # type: ignore + elif self._view: + self._view._add_count(1) + self._children.append(item) item._update_view(self.view) item._parent = self - - if item._has_children() and self._view: - self._view._total_children += len(tuple(item.walk_children())) # type: ignore - elif self._view: - self._view._total_children += 1 return self def remove_item(self, item: Item[Any]) -> Self: @@ -327,11 +329,11 @@ class Container(Item[V]): except ValueError: pass else: - if self._view and self._view._is_layout(): + if self._view: if item._has_children(): - self._view._total_children -= len(tuple(item.walk_children())) # type: ignore + self._view._add_count(-len(tuple(item.walk_children()))) # type: ignore else: - self._view._total_children -= 1 + self._view._add_count(-1) return self def find_item(self, id: int, /) -> Optional[Item[V]]: @@ -361,7 +363,7 @@ class Container(Item[V]): chaining. """ - if self._view and self._view._is_layout(): - self._view._total_children -= len(tuple(self.walk_children())) + if self._view: + self._view._add_count(-len(tuple(self.walk_children()))) self._children.clear() return self diff --git a/discord/ui/section.py b/discord/ui/section.py index 745f91ab3..fc1b770ba 100644 --- a/discord/ui/section.py +++ b/discord/ui/section.py @@ -145,23 +145,24 @@ class Section(Item[V]): TypeError An :class:`Item` or :class:`str` was not passed. ValueError - Maximum number of children has been exceeded (3). + Maximum number of children has been exceeded (3) or (40) + for the entire view. """ if len(self._children) >= 3: - raise ValueError('maximum number of children exceeded') + raise ValueError('maximum number of children exceeded (3)') if not isinstance(item, (Item, str)): raise TypeError(f'expected Item or str not {item.__class__.__name__}') + if self._view: + self._view._add_count(1) + item = item if isinstance(item, Item) else TextDisplay(item) item._update_view(self.view) item._parent = self self._children.append(item) - if self._view: - self._view._total_children += 1 - return self def remove_item(self, item: Item[Any]) -> Self: @@ -182,7 +183,7 @@ class Section(Item[V]): pass else: if self._view: - self._view._total_children -= 1 + self._view._add_count(-1) return self @@ -212,8 +213,8 @@ class Section(Item[V]): This function returns the class instance to allow for fluent-style chaining. """ - if self._view and self._view._is_layout(): - self._view._total_children -= len(self._children) # we don't count the accessory because it is required + if self._view: + self._view._add_count(-len(self._children)) # we don't count the accessory because it is required self._children.clear() return self diff --git a/discord/ui/view.py b/discord/ui/view.py index d2ff034d6..af41285d5 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -319,6 +319,9 @@ class BaseView: self.__timeout = value + def _add_count(self, value: int) -> None: + self._total_children = max(0, self._total_children + value) + @property def children(self) -> List[Item[Self]]: """List[:class:`Item`]: The list of children attached to this view.""" @@ -419,10 +422,7 @@ class BaseView: if item._has_children(): added += len(tuple(item.walk_children())) # type: ignore - if self._is_layout() and self._total_children + added > 40: - raise ValueError('maximum number of children exceeded') - - self._total_children += added + self._add_count(added) self._children.append(item) return self @@ -446,11 +446,7 @@ class BaseView: removed = 1 if item._has_children(): removed += len(tuple(item.walk_children())) # type: ignore - - if self._total_children - removed < 0: - self._total_children = 0 - else: - self._total_children -= removed + self._add_count(-removed) return self @@ -822,6 +818,12 @@ class LayoutView(BaseView): def _is_layout(self) -> bool: return True + def _add_count(self, value: int) -> None: + if self._total_children + value > 40: + raise ValueError('maximum number of children exceeded (40)') + + self._total_children = max(0, self._total_children + value) + def to_components(self): components: List[Dict[str, Any]] = [] for i in self._children: