diff --git a/discord/ui/button.py b/discord/ui/button.py index 457a632c6..1f99a2dc8 100644 --- a/discord/ui/button.py +++ b/discord/ui/button.py @@ -240,8 +240,8 @@ def button( """A decorator that attaches a button to a component. The function being decorated should have three parameters, ``self`` representing - the :class:`discord.ui.View`, the :class:`discord.ui.Button` being pressed and - the :class:`discord.Interaction` you receive. + the :class:`discord.ui.View`, the :class:`discord.Interaction` you receive and + the :class:`discord.ui.Button` being pressed. .. note:: diff --git a/discord/ui/item.py b/discord/ui/item.py index a80875a12..45f27fe20 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -41,7 +41,7 @@ if TYPE_CHECKING: I = TypeVar('I', bound='Item') V = TypeVar('V', bound='View', covariant=True) -ItemCallbackType = Callable[[V, I, Interaction], Coroutine[Any, Any, Any]] +ItemCallbackType = Callable[[V, Interaction, I], Coroutine[Any, Any, Any]] class Item(Generic[V]): diff --git a/discord/ui/select.py b/discord/ui/select.py index 87de5450a..a592356b5 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -305,8 +305,8 @@ def select( """A decorator that attaches a select menu to a component. The function being decorated should have three parameters, ``self`` representing - the :class:`discord.ui.View`, the :class:`discord.ui.Select` being pressed and - the :class:`discord.Interaction` you receive. + the :class:`discord.ui.View`, the :class:`discord.Interaction` you receive and + the :class:`discord.ui.Select` being used. In order to get the selected items that the user has chosen within the callback use :attr:`Select.values`. diff --git a/discord/ui/view.py b/discord/ui/view.py index 4c3e9d886..c0c4296d6 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE. """ from __future__ import annotations -from typing import Any, Callable, ClassVar, Dict, Iterator, List, Optional, Sequence, TYPE_CHECKING, Tuple +from typing import Any, Callable, ClassVar, Coroutine, Dict, Iterator, List, Optional, Sequence, TYPE_CHECKING, Tuple from functools import partial from itertools import groupby @@ -127,6 +127,18 @@ class _ViewWeights: self.weights = [0, 0, 0, 0, 0] +class _ViewCallback: + __slots__ = ('view', 'callback', 'item') + + def __init__(self, callback: ItemCallbackType[Any, Any], view: View, item: Item[View]) -> None: + self.callback: ItemCallbackType[Any, Any] = callback + self.view: View = view + self.item: Item[View] = item + + def __call__(self, interaction: Interaction) -> Coroutine[Any, Any, Any]: + return self.callback(self.view, interaction, self.item) + + class View: """Represents a UI view. @@ -169,7 +181,7 @@ class View: children = [] for func in self.__view_children_items__: item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__) - item.callback = partial(func, self, item) # type: ignore + item.callback = _ViewCallback(func, self, item) item._view = self setattr(self, func.__name__, item) children.append(item) diff --git a/examples/views/confirm.py b/examples/views/confirm.py index cc1164800..09c31ef0f 100644 --- a/examples/views/confirm.py +++ b/examples/views/confirm.py @@ -27,14 +27,14 @@ class Confirm(discord.ui.View): # stop the View from listening to more input. # We also send the user an ephemeral message that we're confirming their choice. @discord.ui.button(label='Confirm', style=discord.ButtonStyle.green) - async def confirm(self, button: discord.ui.Button, interaction: discord.Interaction): + async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('Confirming', ephemeral=True) self.value = True self.stop() # This one is similar to the confirmation button except sets the inner value to `False` @discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey) - async def cancel(self, button: discord.ui.Button, interaction: discord.Interaction): + async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('Cancelling', ephemeral=True) self.value = False self.stop() diff --git a/examples/views/counter.py b/examples/views/counter.py index 9771611ce..df5b3bc9c 100644 --- a/examples/views/counter.py +++ b/examples/views/counter.py @@ -25,7 +25,7 @@ class Counter(discord.ui.View): # When it hits 5, the counter button is disabled and it turns green. # note: The name of the function does not matter to the library @discord.ui.button(label='0', style=discord.ButtonStyle.red) - async def count(self, button: discord.ui.Button, interaction: discord.Interaction): + async def count(self, interaction: discord.Interaction, button: discord.ui.Button): number = int(button.label) if button.label else 0 if number + 1 >= 5: button.style = discord.ButtonStyle.green diff --git a/examples/views/ephemeral.py b/examples/views/ephemeral.py index e0c197c6b..ad1320db9 100644 --- a/examples/views/ephemeral.py +++ b/examples/views/ephemeral.py @@ -23,7 +23,7 @@ class Counter(discord.ui.View): # When it hits 5, the counter button is disabled and it turns green. # note: The name of the function does not matter to the library @discord.ui.button(label='0', style=discord.ButtonStyle.red) - async def count(self, button: discord.ui.Button, interaction: discord.Interaction): + async def count(self, interaction: discord.Interaction, button: discord.ui.Button): number = int(button.label) if button.label else 0 if number + 1 >= 5: button.style = discord.ButtonStyle.green @@ -38,7 +38,7 @@ class EphemeralCounter(discord.ui.View): # When this button is pressed, it will respond with a Counter view that will # give the button presser their own personal button they can press 5 times. @discord.ui.button(label='Click', style=discord.ButtonStyle.blurple) - async def receive(self, button: discord.ui.Button, interaction: discord.Interaction): + async def receive(self, interaction: discord.Interaction, button: discord.ui.Button): # ephemeral=True makes the message hidden from everyone except the button presser await interaction.response.send_message('Enjoy!', view=Counter(), ephemeral=True) diff --git a/examples/views/persistent.py b/examples/views/persistent.py index 1194f5d24..1c410abd2 100644 --- a/examples/views/persistent.py +++ b/examples/views/persistent.py @@ -17,15 +17,15 @@ class PersistentView(discord.ui.View): super().__init__(timeout=None) @discord.ui.button(label='Green', style=discord.ButtonStyle.green, custom_id='persistent_view:green') - async def green(self, button: discord.ui.Button, interaction: discord.Interaction): + async def green(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('This is green.', ephemeral=True) @discord.ui.button(label='Red', style=discord.ButtonStyle.red, custom_id='persistent_view:red') - async def red(self, button: discord.ui.Button, interaction: discord.Interaction): + async def red(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('This is red.', ephemeral=True) @discord.ui.button(label='Grey', style=discord.ButtonStyle.grey, custom_id='persistent_view:grey') - async def grey(self, button: discord.ui.Button, interaction: discord.Interaction): + async def grey(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('This is grey.', ephemeral=True)