diff options
| author | Rapptz <[email protected]> | 2021-04-25 04:57:29 -0400 |
|---|---|---|
| committer | Rapptz <[email protected]> | 2021-05-27 00:53:13 -0400 |
| commit | 98570793e4af8dcfca25621e86fecd85297b0d59 (patch) | |
| tree | efcbe97935fa61126c642f00fdd3ebbc15b61375 /discord/ui/item.py | |
| parent | Fix bug in Embed.__len__ caused by footer without text (diff) | |
| download | discord.py-98570793e4af8dcfca25621e86fecd85297b0d59.tar.xz discord.py-98570793e4af8dcfca25621e86fecd85297b0d59.zip | |
Add initial support for buttons and components
Diffstat (limited to 'discord/ui/item.py')
| -rw-r--r-- | discord/ui/item.py | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/discord/ui/item.py b/discord/ui/item.py new file mode 100644 index 00000000..7726407e --- /dev/null +++ b/discord/ui/item.py @@ -0,0 +1,140 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present Rapptz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +""" + +from __future__ import annotations + +from typing import Any, Callable, Coroutine, Dict, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union +import inspect + +from ..interactions import Interaction + +__all__ = ( + 'Item', +) + +if TYPE_CHECKING: + from ..enums import ComponentType + from .view import View + from ..components import Component + +I = TypeVar('I', bound='Item') +ItemCallbackType = Callable[[Any, I, Interaction], Coroutine[Any, Any, Any]] + + +class Item: + """Represents the base UI item that all UI components inherit from. + + The current UI items supported are: + + - :class:`discord.ui.Button` + """ + + __slots__: Tuple[str, ...] = ( + '_callback', + '_pass_view_arg', + 'group_id', + ) + + __item_repr_attributes__: Tuple[str, ...] = ('group_id',) + + def __init__(self): + self._callback: Optional[ItemCallbackType] = None + self._pass_view_arg = True + self.group_id: Optional[int] = None + + def to_component_dict(self) -> Dict[str, Any]: + raise NotImplementedError + + def copy(self: I) -> I: + raise NotImplementedError + + def refresh_state(self, component: Component) -> None: + return None + + @classmethod + def from_component(cls: Type[I], component: Component) -> I: + return cls() + + @property + def type(self) -> ComponentType: + raise NotImplementedError + + def is_dispatchable(self) -> bool: + return False + + def __repr__(self) -> str: + attrs = ' '.join(f'{key}={getattr(self, key)!r}' for key in self.__item_repr_attributes__) + return f'<{self.__class__.__name__} {attrs}>' + + @property + def callback(self) -> Optional[ItemCallbackType]: + """Returns the underlying callback associated with this interaction.""" + return self._callback + + @callback.setter + def callback(self, value: Optional[ItemCallbackType]): + if value is None: + self._callback = None + return + + # Check if it's a partial function + try: + partial = value.func + except AttributeError: + pass + else: + if not inspect.iscoroutinefunction(value.func): + raise TypeError(f'inner partial function must be a coroutine') + + # Check if the partial is bound + try: + bound_partial = partial.__self__ + except AttributeError: + pass + else: + self._pass_view_arg = not hasattr(bound_partial, '__discord_ui_view__') + + self._callback = value + return + + try: + func_self = value.__self__ + except AttributeError: + pass + else: + if not isinstance(func_self, Item): + raise TypeError(f'callback bound method must be from Item not {func_self!r}') + else: + value = value.__func__ + + if not inspect.iscoroutinefunction(value): + raise TypeError(f'callback must be a coroutine not {value!r}') + + self._callback = value + + async def _do_call(self, view: View, interaction: Interaction): + if self._pass_view_arg: + await self._callback(view, self, interaction) + else: + await self._callback(self, interaction) # type: ignore |