Browse Source

chore: some fixes of bugs reported on the bikeshedding post

pull/10166/head
DA-344 3 months ago
parent
commit
c48c512d88
  1. 11
      discord/ui/action_row.py
  2. 7
      discord/ui/button.py
  3. 90
      discord/ui/container.py
  4. 4
      discord/ui/file.py
  5. 9
      discord/ui/item.py
  6. 4
      discord/ui/media_gallery.py
  7. 4
      discord/ui/section.py
  8. 44
      discord/ui/select.py
  9. 4
      discord/ui/separator.py
  10. 4
      discord/ui/text_display.py
  11. 8
      discord/ui/thumbnail.py
  12. 8
      discord/ui/view.py

11
discord/ui/action_row.py

@ -94,19 +94,20 @@ class ActionRow(Item[V]):
Parameters Parameters
---------- ----------
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this action row. Defaults to ``None``. The ID of this component. This must be unique across the view.
""" """
__action_row_children_items__: ClassVar[List[ItemCallbackType[Any, Any]]] = [] __action_row_children_items__: ClassVar[List[ItemCallbackType[Any, Any]]] = []
__discord_ui_action_row__: ClassVar[bool] = True __discord_ui_action_row__: ClassVar[bool] = True
__pending_view__: ClassVar[bool] = True
def __init__(self, *, id: Optional[str] = None) -> None: def __init__(self, *, id: Optional[int] = None) -> None:
super().__init__() super().__init__()
self.id: str = id or os.urandom(16).hex()
self._children: List[Item[Any]] = self._init_children() self._children: List[Item[Any]] = self._init_children()
self.id = id
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:
super().__init_subclass__() super().__init_subclass__()

7
discord/ui/button.py

@ -83,6 +83,10 @@ class Button(Item[V]):
nor ``custom_id``. nor ``custom_id``.
.. versionadded:: 2.4 .. versionadded:: 2.4
id: Optional[:class:`int`]
The ID of this component. This must be unique across the view.
.. versionadded:: 2.6
""" """
__item_repr_attributes__: Tuple[str, ...] = ( __item_repr_attributes__: Tuple[str, ...] = (
@ -106,6 +110,7 @@ class Button(Item[V]):
emoji: Optional[Union[str, Emoji, PartialEmoji]] = None, emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
row: Optional[int] = None, row: Optional[int] = None,
sku_id: Optional[int] = None, sku_id: Optional[int] = None,
id: Optional[int] = None,
): ):
super().__init__() super().__init__()
if custom_id is not None and (url is not None or sku_id is not None): if custom_id is not None and (url is not None or sku_id is not None):
@ -147,7 +152,7 @@ class Button(Item[V]):
) )
self._parent: Optional[ActionRow] = None self._parent: Optional[ActionRow] = None
self.row = row self.row = row
self.id = custom_id self.id = id
@property @property
def style(self) -> ButtonStyle: def style(self) -> ButtonStyle:

90
discord/ui/container.py

@ -23,10 +23,10 @@ DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Type, TypeVar from typing import TYPE_CHECKING, Any, ClassVar, Coroutine, Dict, List, Literal, Optional, Tuple, Type, TypeVar, Union
from .item import Item from .item import Item, ItemCallbackType
from .view import BaseView, _component_to_item, LayoutView from .view import _component_to_item, LayoutView
from .dynamic import DynamicItem from .dynamic import DynamicItem
from ..enums import ComponentType from ..enums import ComponentType
from ..utils import MISSING from ..utils import MISSING
@ -36,13 +36,26 @@ if TYPE_CHECKING:
from ..colour import Colour, Color from ..colour import Colour, Color
from ..components import Container as ContainerComponent from ..components import Container as ContainerComponent
from ..interactions import Interaction
V = TypeVar('V', bound='LayoutView', covariant=True) V = TypeVar('V', bound='LayoutView', covariant=True)
__all__ = ('Container',) __all__ = ('Container',)
class Container(BaseView, Item[V]): class _ContainerCallback:
__slots__ = ('container', 'callback', 'item')
def __init__(self, callback: ItemCallbackType[Any, 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]):
"""Represents a UI container. """Represents a UI container.
.. versionadded:: 2.6 .. versionadded:: 2.6
@ -66,41 +79,86 @@ class Container(BaseView, Item[V]):
passing an index is advised. For example, row=1 will show passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed) ordering. The row number must be between 0 and 9 (i.e. zero indexed)
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this component. This must be unique across the view. The ID of this component. This must be unique across the view.
""" """
__container_children_items__: ClassVar[List[Union[ItemCallbackType[Any, Any], Item[Any]]]] = []
__pending_view__: ClassVar[bool] = True
def __init__( def __init__(
self, self,
children: List[Item[Any]] = MISSING, children: List[Item[V]] = MISSING,
*, *,
accent_colour: Optional[Colour] = None, accent_colour: Optional[Colour] = None,
accent_color: Optional[Color] = None, accent_color: Optional[Color] = None,
spoiler: bool = False, spoiler: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[str] = None, id: Optional[int] = None,
) -> None: ) -> None:
super().__init__(timeout=None) self._children: List[Item[V]] = self._init_children()
if children is not MISSING: if children is not MISSING:
if len(children) + len(self._children) > 10: if len(children) + len(self._children) > 10:
raise ValueError('maximum number of components exceeded') raise ValueError('maximum number of children exceeded')
self._children.extend(children)
self.spoiler: bool = spoiler self.spoiler: bool = spoiler
self._colour = accent_colour or accent_color self._colour = accent_colour or accent_color
self._view: Optional[V] = None self._view: Optional[V] = None
self._row: Optional[int] = None self.row = row
self._rendered_row: Optional[int] = None self.id = id
self.row: Optional[int] = row
self.id: Optional[str] = id def _init_children(self) -> List[Item[Any]]:
children = []
for raw in self.__container_children_items__:
if isinstance(raw, Item):
children.append(raw)
else:
# 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
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
# guarding it
parent = getattr(raw, '__discord_ui_parent__', None)
if parent is None:
raise RuntimeError(f'{raw.__name__} is not a valid item for a Container')
parent._children.append(item)
# we donnot append it to the children list because technically these buttons and
# selects are not from the container but the action row itself.
return children
def __init_subclass__(cls) -> None:
super().__init_subclass__()
children: Dict[str, Union[ItemCallbackType[Any, Any], Item[Any]]] = {}
for base in reversed(cls.__mro__):
for name, member in base.__dict__.items():
if isinstance(member, Item):
children[name] = member
if hasattr(member, '__discord_ui_model_type__') and hasattr(member, '__discord_ui_parent__'):
children[name] = member
cls.__container_children_items__ = list(children.values())
def _update_children_view(self, view) -> None:
for child in self._children:
child._view = view
if getattr(child, '__pending_view__', False):
# if the item is an action row which child's view can be updated, then update it
child._update_children_view(view) # type: ignore
@property @property
def children(self) -> List[Item[Self]]: def children(self) -> List[Item[V]]:
"""List[:class:`Item`]: The children of this container.""" """List[:class:`Item`]: The children of this container."""
return self._children.copy() return self._children.copy()
@children.setter @children.setter
def children(self, value: List[Item[Any]]) -> None: def children(self, value: List[Item[V]]) -> None:
self._children = value self._children = value
@property @property

4
discord/ui/file.py

@ -59,7 +59,7 @@ class File(Item[V]):
passing an index is advised. For example, row=1 will show passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed) ordering. The row number must be between 0 and 9 (i.e. zero indexed)
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this component. This must be unique across the view. The ID of this component. This must be unique across the view.
""" """
@ -69,7 +69,7 @@ class File(Item[V]):
*, *,
spoiler: bool = False, spoiler: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[str] = None, id: Optional[int] = None,
) -> None: ) -> None:
super().__init__() super().__init__()
self._underlying = FileComponent._raw_construct( self._underlying = FileComponent._raw_construct(

9
discord/ui/item.py

@ -70,7 +70,7 @@ class Item(Generic[V]):
# actually affect the intended purpose of this check because from_component is # actually affect the intended purpose of this check because from_component is
# only called upon edit and we're mainly interested during initial creation time. # only called upon edit and we're mainly interested during initial creation time.
self._provided_custom_id: bool = False self._provided_custom_id: bool = False
self._id: Optional[str] = None self._id: Optional[int] = None
self._max_row: int = 5 if not self._is_v2() else 10 self._max_row: int = 5 if not self._is_v2() else 10
def to_component_dict(self) -> Dict[str, Any]: def to_component_dict(self) -> Dict[str, Any]:
@ -126,14 +126,13 @@ class Item(Generic[V]):
return self._view return self._view
@property @property
def id(self) -> Optional[str]: def id(self) -> Optional[int]:
"""Optional[:class:`str`]: The ID of this component. For non v2 components this is the """Optional[:class:`int`]: The ID of this component.
equivalent to ``custom_id``.
""" """
return self._id return self._id
@id.setter @id.setter
def id(self, value: Optional[str]) -> None: def id(self, value: Optional[int]) -> None:
self._id = value self._id = value
async def callback(self, interaction: Interaction[ClientT]) -> Any: async def callback(self, interaction: Interaction[ClientT]) -> Any:

4
discord/ui/media_gallery.py

@ -60,7 +60,7 @@ class MediaGallery(Item[V]):
passing an index is advised. For example, row=1 will show passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed) ordering. The row number must be between 0 and 9 (i.e. zero indexed)
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this component. This must be unique across the view. The ID of this component. This must be unique across the view.
""" """
@ -69,7 +69,7 @@ class MediaGallery(Item[V]):
items: List[MediaGalleryItem], items: List[MediaGalleryItem],
*, *,
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[str] = None, id: Optional[int] = None,
) -> None: ) -> None:
super().__init__() super().__init__()

4
discord/ui/section.py

@ -59,7 +59,7 @@ class Section(Item[V]):
passing an index is advised. For example, row=1 will show passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed) ordering. The row number must be between 0 and 9 (i.e. zero indexed)
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this component. This must be unique across the view. The ID of this component. This must be unique across the view.
""" """
@ -74,7 +74,7 @@ class Section(Item[V]):
*, *,
accessory: Item[Any], accessory: Item[Any],
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[str] = None, id: Optional[int] = None,
) -> None: ) -> None:
super().__init__() super().__init__()
self._children: List[Item[Any]] = [] self._children: List[Item[Any]] = []

44
discord/ui/select.py

@ -239,6 +239,7 @@ class BaseSelect(Item[V]):
options: List[SelectOption] = MISSING, options: List[SelectOption] = MISSING,
channel_types: List[ChannelType] = MISSING, channel_types: List[ChannelType] = MISSING,
default_values: Sequence[SelectDefaultValue] = MISSING, default_values: Sequence[SelectDefaultValue] = MISSING,
id: Optional[int] = None,
) -> None: ) -> None:
super().__init__() super().__init__()
self._provided_custom_id = custom_id is not MISSING self._provided_custom_id = custom_id is not MISSING
@ -259,7 +260,7 @@ class BaseSelect(Item[V]):
) )
self.row = row self.row = row
self.id = custom_id if custom_id is not MISSING else None self.id = id
self._parent: Optional[ActionRow] = None self._parent: Optional[ActionRow] = None
self._values: List[PossibleValue] = [] self._values: List[PossibleValue] = []
@ -393,6 +394,10 @@ class Select(BaseSelect[V]):
like to control the relative positioning of the row then passing an index is advised. like to control the relative positioning of the row then passing an index is advised.
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 4 (i.e. zero indexed). ordering. The row number must be between 0 and 4 (i.e. zero indexed).
id: Optional[:class:`int`]
The ID of the component. This must be unique across the view.
.. versionadded:: 2.6
""" """
__component_attributes__ = BaseSelect.__component_attributes__ + ('options',) __component_attributes__ = BaseSelect.__component_attributes__ + ('options',)
@ -407,6 +412,7 @@ class Select(BaseSelect[V]):
options: List[SelectOption] = MISSING, options: List[SelectOption] = MISSING,
disabled: bool = False, disabled: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[int] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
self.type, self.type,
@ -417,6 +423,7 @@ class Select(BaseSelect[V]):
disabled=disabled, disabled=disabled,
options=options, options=options,
row=row, row=row,
id=id,
) )
@property @property
@ -548,6 +555,10 @@ class UserSelect(BaseSelect[V]):
like to control the relative positioning of the row then passing an index is advised. like to control the relative positioning of the row then passing an index is advised.
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 4 (i.e. zero indexed). ordering. The row number must be between 0 and 4 (i.e. zero indexed).
id: Optional[:class:`int`]
The ID of the component. This must be unique across the view.
.. versionadded:: 2.6
""" """
__component_attributes__ = BaseSelect.__component_attributes__ + ('default_values',) __component_attributes__ = BaseSelect.__component_attributes__ + ('default_values',)
@ -562,6 +573,7 @@ class UserSelect(BaseSelect[V]):
disabled: bool = False, disabled: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
self.type, self.type,
@ -572,6 +584,7 @@ class UserSelect(BaseSelect[V]):
disabled=disabled, disabled=disabled,
row=row, row=row,
default_values=_handle_select_defaults(default_values, self.type), default_values=_handle_select_defaults(default_values, self.type),
id=id,
) )
@property @property
@ -640,6 +653,10 @@ class RoleSelect(BaseSelect[V]):
like to control the relative positioning of the row then passing an index is advised. like to control the relative positioning of the row then passing an index is advised.
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 4 (i.e. zero indexed). ordering. The row number must be between 0 and 4 (i.e. zero indexed).
id: Optional[:class:`int`]
The ID of the component. This must be unique across the view.
.. versionadded:: 2.6
""" """
__component_attributes__ = BaseSelect.__component_attributes__ + ('default_values',) __component_attributes__ = BaseSelect.__component_attributes__ + ('default_values',)
@ -654,6 +671,7 @@ class RoleSelect(BaseSelect[V]):
disabled: bool = False, disabled: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
self.type, self.type,
@ -664,6 +682,7 @@ class RoleSelect(BaseSelect[V]):
disabled=disabled, disabled=disabled,
row=row, row=row,
default_values=_handle_select_defaults(default_values, self.type), default_values=_handle_select_defaults(default_values, self.type),
id=id,
) )
@property @property
@ -728,6 +747,10 @@ class MentionableSelect(BaseSelect[V]):
like to control the relative positioning of the row then passing an index is advised. like to control the relative positioning of the row then passing an index is advised.
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 4 (i.e. zero indexed). ordering. The row number must be between 0 and 4 (i.e. zero indexed).
id: Optional[:class:`int`]
The ID of the component. This must be unique across the view.
.. versionadded:: 2.6
""" """
__component_attributes__ = BaseSelect.__component_attributes__ + ('default_values',) __component_attributes__ = BaseSelect.__component_attributes__ + ('default_values',)
@ -742,6 +765,7 @@ class MentionableSelect(BaseSelect[V]):
disabled: bool = False, disabled: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
self.type, self.type,
@ -752,6 +776,7 @@ class MentionableSelect(BaseSelect[V]):
disabled=disabled, disabled=disabled,
row=row, row=row,
default_values=_handle_select_defaults(default_values, self.type), default_values=_handle_select_defaults(default_values, self.type),
id=id,
) )
@property @property
@ -822,6 +847,10 @@ class ChannelSelect(BaseSelect[V]):
like to control the relative positioning of the row then passing an index is advised. like to control the relative positioning of the row then passing an index is advised.
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 4 (i.e. zero indexed). ordering. The row number must be between 0 and 4 (i.e. zero indexed).
id: Optional[:class:`int`]
The ID of the component. This must be unique across the view.
.. versionadded:: 2.6
""" """
__component_attributes__ = BaseSelect.__component_attributes__ + ( __component_attributes__ = BaseSelect.__component_attributes__ + (
@ -840,6 +869,7 @@ class ChannelSelect(BaseSelect[V]):
disabled: bool = False, disabled: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None,
) -> None: ) -> None:
super().__init__( super().__init__(
self.type, self.type,
@ -851,6 +881,7 @@ class ChannelSelect(BaseSelect[V]):
row=row, row=row,
channel_types=channel_types, channel_types=channel_types,
default_values=_handle_select_defaults(default_values, self.type), default_values=_handle_select_defaults(default_values, self.type),
id=id,
) )
@property @property
@ -902,6 +933,7 @@ def select(
max_values: int = ..., max_values: int = ...,
disabled: bool = ..., disabled: bool = ...,
row: Optional[int] = ..., row: Optional[int] = ...,
id: Optional[int] = ...,
) -> SelectCallbackDecorator[V, SelectT]: ) -> SelectCallbackDecorator[V, SelectT]:
... ...
@ -919,6 +951,7 @@ def select(
disabled: bool = ..., disabled: bool = ...,
default_values: Sequence[ValidDefaultValues] = ..., default_values: Sequence[ValidDefaultValues] = ...,
row: Optional[int] = ..., row: Optional[int] = ...,
id: Optional[int] = ...,
) -> SelectCallbackDecorator[V, UserSelectT]: ) -> SelectCallbackDecorator[V, UserSelectT]:
... ...
@ -936,6 +969,7 @@ def select(
disabled: bool = ..., disabled: bool = ...,
default_values: Sequence[ValidDefaultValues] = ..., default_values: Sequence[ValidDefaultValues] = ...,
row: Optional[int] = ..., row: Optional[int] = ...,
id: Optional[int] = ...,
) -> SelectCallbackDecorator[V, RoleSelectT]: ) -> SelectCallbackDecorator[V, RoleSelectT]:
... ...
@ -953,6 +987,7 @@ def select(
disabled: bool = ..., disabled: bool = ...,
default_values: Sequence[ValidDefaultValues] = ..., default_values: Sequence[ValidDefaultValues] = ...,
row: Optional[int] = ..., row: Optional[int] = ...,
id: Optional[int] = ...,
) -> SelectCallbackDecorator[V, ChannelSelectT]: ) -> SelectCallbackDecorator[V, ChannelSelectT]:
... ...
@ -970,6 +1005,7 @@ def select(
disabled: bool = ..., disabled: bool = ...,
default_values: Sequence[ValidDefaultValues] = ..., default_values: Sequence[ValidDefaultValues] = ...,
row: Optional[int] = ..., row: Optional[int] = ...,
id: Optional[int] = ...,
) -> SelectCallbackDecorator[V, MentionableSelectT]: ) -> SelectCallbackDecorator[V, MentionableSelectT]:
... ...
@ -986,6 +1022,7 @@ def select(
disabled: bool = False, disabled: bool = False,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[int] = None,
) -> SelectCallbackDecorator[V, BaseSelectT]: ) -> SelectCallbackDecorator[V, BaseSelectT]:
"""A decorator that attaches a select menu to a component. """A decorator that attaches a select menu to a component.
@ -1065,6 +1102,10 @@ def select(
Number of items must be in range of ``min_values`` and ``max_values``. Number of items must be in range of ``min_values`` and ``max_values``.
.. versionadded:: 2.4 .. versionadded:: 2.4
id: Optional[:class:`int`]
The ID of the component. This must be unique across the view.
.. versionadded:: 2.6
""" """
def decorator(func: ItemCallbackType[V, BaseSelectT]) -> ItemCallbackType[V, BaseSelectT]: def decorator(func: ItemCallbackType[V, BaseSelectT]) -> ItemCallbackType[V, BaseSelectT]:
@ -1083,6 +1124,7 @@ def select(
'min_values': min_values, 'min_values': min_values,
'max_values': max_values, 'max_values': max_values,
'disabled': disabled, 'disabled': disabled,
'id': id,
} }
if issubclass(callback_cls, Select): if issubclass(callback_cls, Select):
func.__discord_ui_model_kwargs__['options'] = options func.__discord_ui_model_kwargs__['options'] = options

4
discord/ui/separator.py

@ -58,7 +58,7 @@ class Separator(Item[V]):
passing an index is advised. For example, row=1 will show passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed) ordering. The row number must be between 0 and 9 (i.e. zero indexed)
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this component. This must be unique across the view. The ID of this component. This must be unique across the view.
""" """
@ -68,7 +68,7 @@ class Separator(Item[V]):
visible: bool = True, visible: bool = True,
spacing: SeparatorSize = SeparatorSize.small, spacing: SeparatorSize = SeparatorSize.small,
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[str] = None, id: Optional[int] = None,
) -> None: ) -> None:
super().__init__() super().__init__()
self._underlying = SeparatorComponent._raw_construct( self._underlying = SeparatorComponent._raw_construct(

4
discord/ui/text_display.py

@ -55,11 +55,11 @@ class TextDisplay(Item[V]):
passing an index is advised. For example, row=1 will show passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed) ordering. The row number must be between 0 and 9 (i.e. zero indexed)
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this component. This must be unique across the view. The ID of this component. This must be unique across the view.
""" """
def __init__(self, content: str, *, row: Optional[int] = None, id: Optional[str] = None) -> None: def __init__(self, content: str, *, row: Optional[int] = None, id: Optional[int] = None) -> None:
super().__init__() super().__init__()
self.content: str = content self.content: str = content

8
discord/ui/thumbnail.py

@ -48,8 +48,8 @@ class Thumbnail(Item[V]):
Parameters Parameters
---------- ----------
media: Union[:class:`str`, :class:`discord.UnfurledMediaItem`] media: Union[:class:`str`, :class:`discord.UnfurledMediaItem`]
The media of the thumbnail. This can be a string that points to a local The media of the thumbnail. This can be a URL or a reference
attachment uploaded within this item. URLs must match the ``attachment://file-name.extension`` to an attachment that matches the ``attachment://filename.extension``
structure. structure.
description: Optional[:class:`str`] description: Optional[:class:`str`]
The description of this thumbnail. Defaults to ``None``. The description of this thumbnail. Defaults to ``None``.
@ -62,7 +62,7 @@ class Thumbnail(Item[V]):
passing an index is advised. For example, row=1 will show passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed) ordering. The row number must be between 0 and 9 (i.e. zero indexed)
id: Optional[:class:`str`] id: Optional[:class:`int`]
The ID of this component. This must be unique across the view. The ID of this component. This must be unique across the view.
""" """
@ -73,7 +73,7 @@ class Thumbnail(Item[V]):
description: Optional[str] = None, description: Optional[str] = None,
spoiler: bool = False, spoiler: bool = False,
row: Optional[int] = None, row: Optional[int] = None,
id: Optional[str] = None, id: Optional[int] = None,
) -> None: ) -> None:
super().__init__() super().__init__()

8
discord/ui/view.py

@ -223,6 +223,8 @@ class BaseView:
parent = getattr(raw, '__discord_ui_parent__', None) parent = getattr(raw, '__discord_ui_parent__', None)
if parent and parent._view is None: if parent and parent._view is None:
parent._view = self parent._view = self
if getattr(raw, '__pending_view__', False):
raw._update_children_view(self) # type: ignore
children.append(raw) children.append(raw)
else: else:
item: Item = raw.__discord_ui_model_type__(**raw.__discord_ui_model_kwargs__) item: Item = raw.__discord_ui_model_type__(**raw.__discord_ui_model_kwargs__)
@ -581,6 +583,8 @@ class View(BaseView): # NOTE: maybe add a deprecation warning in favour of Layo
for name, member in base.__dict__.items(): for name, member in base.__dict__.items():
if hasattr(member, '__discord_ui_model_type__'): if hasattr(member, '__discord_ui_model_type__'):
children[name] = member children[name] = member
elif isinstance(member, Item) and member._is_v2():
raise RuntimeError(f'{name} cannot be added to this View')
if len(children) > 25: if len(children) > 25:
raise TypeError('View cannot have more than 25 children') raise TypeError('View cannot have more than 25 children')
@ -707,10 +711,14 @@ class LayoutView(BaseView):
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:
children: Dict[str, Item[Any]] = {} children: Dict[str, Item[Any]] = {}
row = 0
for base in reversed(cls.__mro__): for base in reversed(cls.__mro__):
for name, member in base.__dict__.items(): for name, member in base.__dict__.items():
if isinstance(member, Item): if isinstance(member, Item):
member._rendered_row = member._row or row
children[name] = member children[name] = member
row += 1
elif hasattr(member, '__discord_ui_model_type__') and getattr(member, '__discord_ui_parent__', None): elif hasattr(member, '__discord_ui_model_type__') and getattr(member, '__discord_ui_parent__', None):
children[name] = member children[name] = member

Loading…
Cancel
Save