mirror of
https://github.com/Rapptz/discord.py.git
synced 2026-09-21 03:27: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:
|
def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None:
|
||||||
child_index = self._children.index(base)
|
child_index = self._children.index(base)
|
||||||
self._children[child_index] = new # type: ignore
|
self._children[child_index] = new # type: ignore
|
||||||
|
base._detach_view()
|
||||||
|
new._update_view(self.view)
|
||||||
|
new._parent = self
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self):
|
def width(self):
|
||||||
@@ -299,6 +302,7 @@ class ActionRow(Item[V]):
|
|||||||
if self._view:
|
if self._view:
|
||||||
self._view._add_count(-1)
|
self._view._add_count(-1)
|
||||||
self._weight -= item.width
|
self._weight -= item.width
|
||||||
|
item._detach_view()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -330,6 +334,8 @@ class ActionRow(Item[V]):
|
|||||||
"""
|
"""
|
||||||
if self._view:
|
if self._view:
|
||||||
self._view._add_count(-len(self._children))
|
self._view._add_count(-len(self._children))
|
||||||
|
for item in self._children:
|
||||||
|
item._detach_view()
|
||||||
self._children.clear()
|
self._children.clear()
|
||||||
self._weight = 0
|
self._weight = 0
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -197,6 +197,9 @@ class Container(Item[V]):
|
|||||||
def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None:
|
def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None:
|
||||||
child_index = self._children.index(base)
|
child_index = self._children.index(base)
|
||||||
self._children[child_index] = new # type: ignore
|
self._children[child_index] = new # type: ignore
|
||||||
|
base._detach_view()
|
||||||
|
new._update_view(self.view)
|
||||||
|
new._parent = self
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def children(self) -> List[Item[V]]:
|
def children(self) -> List[Item[V]]:
|
||||||
@@ -336,6 +339,7 @@ class Container(Item[V]):
|
|||||||
else:
|
else:
|
||||||
if self._view:
|
if self._view:
|
||||||
self._view._add_count(-item._total_count)
|
self._view._add_count(-item._total_count)
|
||||||
|
item._detach_view()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def find_item(self, id: int, /) -> Optional[Item[V]]:
|
def find_item(self, id: int, /) -> Optional[Item[V]]:
|
||||||
@@ -367,5 +371,7 @@ class Container(Item[V]):
|
|||||||
|
|
||||||
if self._view:
|
if self._view:
|
||||||
self._view._add_count(-len(tuple(self.walk_children())))
|
self._view._add_count(-len(tuple(self.walk_children())))
|
||||||
|
for item in self._children:
|
||||||
|
item._detach_view()
|
||||||
self._children.clear()
|
self._children.clear()
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -206,6 +206,10 @@ class Item(Generic[V]):
|
|||||||
def _update_view(self, view) -> None:
|
def _update_view(self, view) -> None:
|
||||||
self._view = view
|
self._view = view
|
||||||
|
|
||||||
|
def _detach_view(self) -> None:
|
||||||
|
self._update_view(None)
|
||||||
|
self._parent = None
|
||||||
|
|
||||||
def copy(self) -> Self:
|
def copy(self) -> Self:
|
||||||
return copy.deepcopy(self)
|
return copy.deepcopy(self)
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ class Section(Item[V]):
|
|||||||
else:
|
else:
|
||||||
if self._view:
|
if self._view:
|
||||||
self._view._add_count(-1)
|
self._view._add_count(-1)
|
||||||
|
item._detach_view()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -244,6 +245,8 @@ class Section(Item[V]):
|
|||||||
if self._view:
|
if self._view:
|
||||||
self._view._add_count(-len(self._children)) # we don't count the accessory because it is required
|
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()
|
self._children.clear()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ import discord
|
|||||||
import pytest
|
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():
|
def test_add_item_with_full_row():
|
||||||
view = discord.ui.View()
|
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 view.total_children_count == max_item_limit - 1
|
||||||
assert row.view is None
|
assert row.view is None
|
||||||
assert all(item.view is None for item in row.children)
|
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