Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 23 additions & 18 deletions discord/ui/action_row.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@

from __future__ import annotations

import copy
from typing import (
TYPE_CHECKING,
Any,
Callable,
ClassVar,
Coroutine,
Dict,
Generator,
List,
Expand All @@ -42,7 +42,7 @@
overload,
)

from .item import Item, ContainedItemCallbackType as ItemCallbackType
from .item import Item, ContainedItemCallbackType as ItemCallbackType, _ItemCallback
from .button import Button, button as _button
from .select import select as _select, Select, UserSelect, RoleSelect, ChannelSelect, MentionableSelect
from ..components import ActionRow as ActionRowComponent
Expand All @@ -65,7 +65,6 @@
)
from ..emoji import Emoji
from ..components import SelectOption
from ..interactions import Interaction
from .container import Container
from .dynamic import DynamicItem

Expand All @@ -77,18 +76,6 @@
__all__ = ('ActionRow',)


class _ActionRowCallback:
__slots__ = ('row', 'callback', 'item')

def __init__(self, callback: ItemCallbackType[S, Any], row: ActionRow, item: Item[Any]) -> None:
self.callback: ItemCallbackType[Any, Any] = callback
self.row: ActionRow = row
self.item: Item[Any] = item

def __call__(self, interaction: Interaction) -> Coroutine[Any, Any, Any]:
return self.callback(self.row, interaction, self.item)


class ActionRow(Item[V]):
r"""Represents a UI action row.

Expand Down Expand Up @@ -143,8 +130,9 @@ def __init__(
) -> None:
super().__init__()
self._children: List[Item[V]] = self._init_children()
self._children.extend(children)
self._weight: int = sum(i.width for i in self._children)
for child in children:
self.add_item(child)

if self._weight > 5:
raise ValueError('maximum number of children exceeded')
Expand Down Expand Up @@ -173,8 +161,8 @@ def _init_children(self) -> List[Item[Any]]:

for func in self.__action_row_children_items__:
item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__)
item.callback = _ActionRowCallback(func, self, item) # type: ignore
item._parent = getattr(func, '__discord_ui_parent__', self)
item.callback = _ItemCallback(func, self, item) # type: ignore
item._parent = self
setattr(self, func.__name__, item)
children.append(item)
return children
Expand All @@ -184,6 +172,23 @@ def _update_view(self, view) -> None:
for child in self._children:
child._view = view

def copy(self) -> ActionRow[V]:
new = copy.copy(self)
children = []
for child in new._children:
newch = child.copy()
newch._parent = new
if isinstance(newch.callback, _ItemCallback):
newch.callback.parent = new
children.append(newch)
new._children = children
new._parent = self._parent
new._update_view(self.view)
return new

def __deepcopy__(self, memo) -> ActionRow[V]:
return self.copy()

def _has_children(self):
return True

Expand Down
5 changes: 4 additions & 1 deletion discord/ui/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import os


from .item import Item, ContainedItemCallbackType as ItemCallbackType
from .item import Item, ContainedItemCallbackType as ItemCallbackType, _ItemCallback
from ..enums import ButtonStyle, ComponentType
from ..partial_emoji import PartialEmoji, _EmojiTag
from ..components import Button as ButtonComponent
Expand Down Expand Up @@ -304,6 +304,9 @@ def copy(self) -> Self:
sku_id=self.sku_id,
id=self.id,
)
if isinstance(new.callback, _ItemCallback):
new.callback.item = new
new._update_view(self.view)
return new

def __deepcopy__(self, memo) -> Self:
Expand Down
27 changes: 11 additions & 16 deletions discord/ui/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
TYPE_CHECKING,
Any,
ClassVar,
Coroutine,
Dict,
Generator,
List,
Expand All @@ -39,7 +38,7 @@
Union,
)

from .item import Item, ContainedItemCallbackType as ItemCallbackType
from .item import Item, ContainedItemCallbackType as ItemCallbackType, _ItemCallback
from .view import _component_to_item, LayoutView
from ..enums import ComponentType
from ..utils import get as _utils_get
Expand All @@ -49,7 +48,6 @@
from typing_extensions import Self

from ..components import Container as ContainerComponent
from ..interactions import Interaction
from .dynamic import DynamicItem

S = TypeVar('S', bound='Container', covariant=True)
Expand All @@ -58,18 +56,6 @@
__all__ = ('Container',)


class _ContainerCallback:
__slots__ = ('container', 'callback', 'item')

def __init__(self, callback: ItemCallbackType[S, Any], container: Container, item: Item[Any]) -> None:
self.callback: ItemCallbackType[Any, Any] = callback
self.container: Container = container
self.item: Item[Any] = item

def __call__(self, interaction: Interaction) -> Coroutine[Any, Any, Any]:
return self.callback(self.container, interaction, self.item)


class Container(Item[V]):
r"""Represents a UI container.

Expand Down Expand Up @@ -163,7 +149,7 @@ def _init_children(self) -> List[Item[Any]]:
# action rows can be created inside containers, and then callbacks can exist here
# so we create items based off them
item: Item = raw.__discord_ui_model_type__(**raw.__discord_ui_model_kwargs__)
item.callback = _ContainerCallback(raw, self, item) # type: ignore
item.callback = _ItemCallback(raw, self, item) # type: ignore
setattr(self, raw.__name__, item)
# this should not fail because in order for a function to be here it should be from
# an action row and must have passed the check in __init_subclass__, but still
Expand Down Expand Up @@ -196,6 +182,15 @@ def _update_view(self, view) -> bool:
child._update_view(view)
return True

def copy(self) -> Container[V]:
new = copy.deepcopy(self)
for child in new._children:
newch = child.copy()
newch._parent = new
new._parent = self._parent
new._update_view(self.view)
return new

def _has_children(self):
return True

Expand Down
15 changes: 15 additions & 0 deletions discord/ui/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@
ContainedItemCallbackType = Callable[[C, Interaction[Any], I], Coroutine[Any, Any, Any]]


class _ItemCallback:
__slots__ = ('parent', 'callback', 'item')

def __init__(self, callback: ContainedItemCallbackType[Any, Any], parent: Any, item: Item[Any]) -> None:
self.callback: ItemCallbackType[Any, Any] = callback
self.parent: Any = parent
self.item: Item[Any] = item

def __repr__(self) -> str:
return f'<ItemCallback callback={self.callback!r} parent={self.parent!r} item={self.item!r}>'

def __call__(self, interaction: Interaction) -> Coroutine[Any, Any, Any]:
return self.callback(self.parent, interaction, self.item)


class Item(Generic[V]):
"""Represents the base UI item that all UI components inherit from.

Expand Down
13 changes: 11 additions & 2 deletions discord/ui/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@
Sequence,
)
from contextvars import ContextVar
import copy
import inspect
import os

from .item import Item, ContainedItemCallbackType as ItemCallbackType
from .item import Item, ContainedItemCallbackType as ItemCallbackType, _ItemCallback
from ..enums import ChannelType, ComponentType, SelectDefaultValueType
from ..partial_emoji import PartialEmoji
from ..emoji import Emoji
Expand Down Expand Up @@ -70,7 +71,7 @@
)

if TYPE_CHECKING:
from typing_extensions import TypeAlias, TypeGuard
from typing_extensions import TypeAlias, TypeGuard, Self

from .view import BaseView
from .action_row import ActionRow
Expand Down Expand Up @@ -269,6 +270,14 @@ def __init__(
self.row = row
self._values: List[PossibleValue] = []

def copy(self) -> Self:
new = copy.copy(self)
if isinstance(new.callback, _ItemCallback):
new.callback.item = new
new._parent = self._parent
new._update_view(self.view)
return new

@property
def id(self) -> Optional[int]:
"""Optional[:class:`int`]: The ID of this select."""
Expand Down
21 changes: 4 additions & 17 deletions discord/ui/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
Any,
Callable,
ClassVar,
Coroutine,
Dict,
Generator,
Iterator,
Expand All @@ -50,7 +49,7 @@
import time
import os

from .item import Item, ItemCallbackType
from .item import Item, ItemCallbackType, _ItemCallback
from .select import Select
from .dynamic import DynamicItem
from ..components import (
Expand Down Expand Up @@ -207,18 +206,6 @@ def clear(self) -> None:
self.weights = [0, 0, 0, 0, 0]


class _ViewCallback:
__slots__ = ('view', 'callback', 'item')

def __init__(self, callback: ItemCallbackType[Any, Any], view: BaseView, item: Item[BaseView]) -> None:
self.callback: ItemCallbackType[Any, Any] = callback
self.view: BaseView = view
self.item: Item[BaseView] = item

def __call__(self, interaction: Interaction) -> Coroutine[Any, Any, Any]:
return self.callback(self.view, interaction, self.item)


class BaseView:
__discord_ui_view__: ClassVar[bool] = False
__discord_ui_modal__: ClassVar[bool] = False
Expand Down Expand Up @@ -252,13 +239,13 @@ def _init_children(self) -> List[Item[Self]]:
item._update_view(self)
parent = getattr(item, '__discord_ui_parent__', None)
if parent and parent._view is None:
parent._view = self
parent._update_view(self)
children.append(item)
parents[raw] = item
else:
item: Item = raw.__discord_ui_model_type__(**raw.__discord_ui_model_kwargs__)
item.callback = _ViewCallback(raw, self, item) # type: ignore
item._view = self
item.callback = _ItemCallback(raw, self, item) # type: ignore
item._update_view(self)
if isinstance(item, Select):
item.options = [option.copy() for option in item.options]
setattr(self, raw.__name__, item)
Expand Down
Loading