mirror of
https://github.com/Rapptz/discord.py.git
synced 2026-09-20 19:17:42 +00:00
Fix nested UI item parent state
This commit is contained in:
@@ -203,6 +203,9 @@ class ActionRow(Item[V]):
|
||||
def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None:
|
||||
child_index = self._children.index(base)
|
||||
self._children[child_index] = new # type: ignore
|
||||
base._detach_view()
|
||||
new._update_view(self.view)
|
||||
new._parent = self
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
@@ -299,6 +302,7 @@ class ActionRow(Item[V]):
|
||||
if self._view:
|
||||
self._view._add_count(-1)
|
||||
self._weight -= item.width
|
||||
item._detach_view()
|
||||
|
||||
return self
|
||||
|
||||
@@ -330,6 +334,8 @@ class ActionRow(Item[V]):
|
||||
"""
|
||||
if self._view:
|
||||
self._view._add_count(-len(self._children))
|
||||
for item in self._children:
|
||||
item._detach_view()
|
||||
self._children.clear()
|
||||
self._weight = 0
|
||||
return self
|
||||
|
||||
@@ -197,6 +197,9 @@ class Container(Item[V]):
|
||||
def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None:
|
||||
child_index = self._children.index(base)
|
||||
self._children[child_index] = new # type: ignore
|
||||
base._detach_view()
|
||||
new._update_view(self.view)
|
||||
new._parent = self
|
||||
|
||||
@property
|
||||
def children(self) -> List[Item[V]]:
|
||||
@@ -336,6 +339,7 @@ class Container(Item[V]):
|
||||
else:
|
||||
if self._view:
|
||||
self._view._add_count(-item._total_count)
|
||||
item._detach_view()
|
||||
return self
|
||||
|
||||
def find_item(self, id: int, /) -> Optional[Item[V]]:
|
||||
@@ -367,5 +371,7 @@ class Container(Item[V]):
|
||||
|
||||
if self._view:
|
||||
self._view._add_count(-len(tuple(self.walk_children())))
|
||||
for item in self._children:
|
||||
item._detach_view()
|
||||
self._children.clear()
|
||||
return self
|
||||
|
||||
@@ -206,6 +206,10 @@ class Item(Generic[V]):
|
||||
def _update_view(self, view) -> None:
|
||||
self._view = view
|
||||
|
||||
def _detach_view(self) -> None:
|
||||
self._update_view(None)
|
||||
self._parent = None
|
||||
|
||||
def copy(self) -> Self:
|
||||
return copy.deepcopy(self)
|
||||
|
||||
|
||||
@@ -212,6 +212,7 @@ class Section(Item[V]):
|
||||
else:
|
||||
if self._view:
|
||||
self._view._add_count(-1)
|
||||
item._detach_view()
|
||||
|
||||
return self
|
||||
|
||||
@@ -244,6 +245,8 @@ class Section(Item[V]):
|
||||
if self._view:
|
||||
self._view._add_count(-len(self._children)) # we don't count the accessory because it is required
|
||||
|
||||
for item in self._children:
|
||||
item._detach_view()
|
||||
self._children.clear()
|
||||
return self
|
||||
|
||||
|
||||
@@ -28,6 +28,12 @@ import discord
|
||||
import pytest
|
||||
|
||||
|
||||
class DynamicButton(discord.ui.DynamicItem[discord.ui.Button], template=r'dynamic:(?P<number>[0-9]+)'):
|
||||
@classmethod
|
||||
async def from_custom_id(cls, interaction, item, match):
|
||||
return cls(discord.ui.Button(label='Dynamic', custom_id=match.group(0)))
|
||||
|
||||
|
||||
def test_add_item_with_full_row():
|
||||
view = discord.ui.View()
|
||||
|
||||
@@ -86,3 +92,91 @@ def test_layout_view_add_item_with_too_many_children():
|
||||
assert view.total_children_count == max_item_limit - 1
|
||||
assert row.view is None
|
||||
assert all(item.view is None for item in row.children)
|
||||
|
||||
|
||||
def test_section_remove_item_clears_view_and_parent():
|
||||
view = discord.ui.LayoutView()
|
||||
section = discord.ui.Section('test', accessory=discord.ui.Button(label='Test'))
|
||||
view.add_item(section)
|
||||
item = section.children[0]
|
||||
|
||||
section.remove_item(item)
|
||||
|
||||
assert item.view is None
|
||||
assert item.parent is None
|
||||
|
||||
|
||||
def test_action_row_remove_item_clears_view_and_parent():
|
||||
view = discord.ui.LayoutView()
|
||||
row = discord.ui.ActionRow()
|
||||
item = discord.ui.Button(label='Test')
|
||||
row.add_item(item)
|
||||
view.add_item(row)
|
||||
|
||||
row.remove_item(item)
|
||||
|
||||
assert item.view is None
|
||||
assert item.parent is None
|
||||
|
||||
|
||||
def test_container_remove_item_clears_view_and_parent():
|
||||
view = discord.ui.LayoutView()
|
||||
container = discord.ui.Container()
|
||||
item = discord.ui.TextDisplay('test')
|
||||
container.add_item(item)
|
||||
view.add_item(container)
|
||||
|
||||
container.remove_item(item)
|
||||
|
||||
assert item.view is None
|
||||
assert item.parent is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_action_row_dynamic_item_swap_preserves_parent_checks():
|
||||
calls = []
|
||||
|
||||
class Row(discord.ui.ActionRow):
|
||||
async def interaction_check(self, interaction):
|
||||
calls.append('row')
|
||||
return True
|
||||
|
||||
base = discord.ui.Button(label='Dynamic', custom_id='dynamic:1')
|
||||
row = Row(base)
|
||||
view = discord.ui.LayoutView()
|
||||
view.add_item(row)
|
||||
item = DynamicButton(discord.ui.Button(label='Dynamic', custom_id='dynamic:1'))
|
||||
|
||||
row._swap_item(base, item, 'dynamic:1')
|
||||
await item._run_checks(None)
|
||||
|
||||
assert item.view is view
|
||||
assert item.parent is row
|
||||
assert base.view is None
|
||||
assert base.parent is None
|
||||
assert calls == ['row']
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_container_dynamic_item_swap_preserves_parent_checks():
|
||||
calls = []
|
||||
|
||||
class Container(discord.ui.Container):
|
||||
async def interaction_check(self, interaction):
|
||||
calls.append('container')
|
||||
return True
|
||||
|
||||
base = discord.ui.ActionRow(discord.ui.Button(label='Dynamic', custom_id='dynamic:1'))
|
||||
container = Container(base)
|
||||
view = discord.ui.LayoutView()
|
||||
view.add_item(container)
|
||||
item = DynamicButton(discord.ui.Button(label='Dynamic', custom_id='dynamic:1'))
|
||||
|
||||
container._swap_item(base, item, 'dynamic:1')
|
||||
await item._run_checks(None)
|
||||
|
||||
assert item.view is view
|
||||
assert item.parent is container
|
||||
assert base.view is None
|
||||
assert base.parent is None
|
||||
assert calls == ['container']
|
||||
|
||||
Reference in New Issue
Block a user