mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
Add global buttons to base menus (#5683)
Co-authored-by: Kowlin <10947836+Kowlin@users.noreply.github.com>
This commit is contained in:
parent
aaeb1b5daa
commit
b0a3f00f41
@ -35,7 +35,7 @@ Embed Helpers
|
||||
:members:
|
||||
:exclude-members: randomize_color
|
||||
|
||||
Reaction Menus
|
||||
Menus
|
||||
==============
|
||||
|
||||
.. automodule:: redbot.core.utils.menus
|
||||
|
||||
@ -142,6 +142,7 @@ class Red(
|
||||
schema_version=0,
|
||||
datarequests__allow_user_requests=True,
|
||||
datarequests__user_requests_are_strict=True,
|
||||
use_buttons=False,
|
||||
)
|
||||
|
||||
self._config.register_guild(
|
||||
@ -1351,6 +1352,17 @@ class Red(
|
||||
global_setting = await self._config.embeds()
|
||||
return global_setting
|
||||
|
||||
async def use_buttons(self) -> bool:
|
||||
"""
|
||||
Determines whether the bot owner has enabled use of buttons instead of
|
||||
reactions for basic menus.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
return await self._config.use_buttons()
|
||||
|
||||
async def is_owner(self, user: Union[discord.User, discord.Member], /) -> bool:
|
||||
"""
|
||||
Determines if the user should be considered a bot owner.
|
||||
|
||||
@ -3621,6 +3621,31 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
else:
|
||||
await ctx.send(_("Server prefixes set."))
|
||||
|
||||
@_set.command(name="usebuttons")
|
||||
async def use_buttons(self, ctx: commands.Context, use_buttons: bool = None):
|
||||
"""
|
||||
Set a global bot variable for using buttons in menus.
|
||||
|
||||
When enabled, all usage of cores menus API will use buttons instead of reactions.
|
||||
|
||||
This defaults to False.
|
||||
Using this without a setting will toggle.
|
||||
|
||||
**Examples:**
|
||||
- `[p]set usebuttons True` - Enables using buttons.
|
||||
- `[p]helpset usebuttons` - Toggles the value.
|
||||
|
||||
**Arguments:**
|
||||
- `[use_buttons]` - Whether to use buttons. Leave blank to toggle.
|
||||
"""
|
||||
if use_buttons is None:
|
||||
use_buttons = not await ctx.bot._config.use_buttons()
|
||||
await ctx.bot._config.use_buttons.set(use_buttons)
|
||||
if use_buttons:
|
||||
await ctx.send(_("I will use buttons on basic menus."))
|
||||
else:
|
||||
await ctx.send(_("I will not use buttons on basic menus."))
|
||||
|
||||
@commands.group()
|
||||
@checks.is_owner()
|
||||
async def helpset(self, ctx: commands.Context):
|
||||
|
||||
@ -6,18 +6,43 @@ import asyncio
|
||||
import contextlib
|
||||
import functools
|
||||
from types import MappingProxyType
|
||||
from typing import Callable, Iterable, List, Mapping, Optional, TypeVar, Union
|
||||
from typing import Callable, Dict, Iterable, List, Mapping, Optional, TypeVar, Union
|
||||
|
||||
import discord
|
||||
|
||||
from .. import commands
|
||||
from .predicates import ReactionPredicate
|
||||
from .views import SimpleMenu, _SimplePageSource
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_PageList = TypeVar("_PageList", List[str], List[discord.Embed])
|
||||
_ReactableEmoji = Union[str, discord.Emoji]
|
||||
_ControlCallable = Callable[[commands.Context, _PageList, discord.Message, int, float, str], _T]
|
||||
|
||||
_active_menus: Dict[int, SimpleMenu] = {}
|
||||
|
||||
|
||||
class _GenericButton(discord.ui.Button):
|
||||
def __init__(self, emoji: Union[str, discord.PartialEmoji], func):
|
||||
super().__init__(
|
||||
emoji=discord.PartialEmoji.from_str(emoji), style=discord.ButtonStyle.grey
|
||||
)
|
||||
self.func = func
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
ctx = self.view.ctx
|
||||
pages = self.view.source.entries
|
||||
controls = None
|
||||
message = self.view.message
|
||||
page = self.view.current_page
|
||||
timeout = self.view.timeout
|
||||
emoji = self.emoji
|
||||
try:
|
||||
await self.func(ctx, pages, controls, message, page, timeout, emoji)
|
||||
except Exception:
|
||||
pass
|
||||
await interaction.response.defer()
|
||||
|
||||
|
||||
async def menu(
|
||||
ctx: commands.Context,
|
||||
@ -64,6 +89,18 @@ async def menu(
|
||||
RuntimeError
|
||||
If either of the notes above are violated
|
||||
"""
|
||||
if message is not None and message.id in _active_menus:
|
||||
# prevents the expected callback from going any further
|
||||
# our custom button will always pass the message the view is
|
||||
# attached to, allowing one to send multiple menus on the same
|
||||
# context.
|
||||
view = _active_menus[message.id]
|
||||
if pages != view.source.entries:
|
||||
view._source = _SimplePageSource(pages)
|
||||
new_page = await view.get_page(page)
|
||||
view.current_page = page
|
||||
await view.message.edit(**new_page)
|
||||
return
|
||||
if not isinstance(pages[0], (discord.Embed, str)):
|
||||
raise RuntimeError("Pages must be of type discord.Embed or str")
|
||||
if not all(isinstance(x, discord.Embed) for x in pages) and not all(
|
||||
@ -81,6 +118,52 @@ async def menu(
|
||||
maybe_coro = value.func
|
||||
if not asyncio.iscoroutinefunction(maybe_coro):
|
||||
raise RuntimeError("Function must be a coroutine")
|
||||
|
||||
if await ctx.bot.use_buttons() and message is None:
|
||||
# Only send the button version if `message` is None
|
||||
# This is because help deals with this menu in weird ways
|
||||
# where the original message is already sent prior to starting.
|
||||
# This is not normally the way we recommend sending this because
|
||||
# internally we already include the emojis we expect.
|
||||
if controls == DEFAULT_CONTROLS:
|
||||
view = SimpleMenu(pages)
|
||||
await view.start(ctx)
|
||||
await view.wait()
|
||||
return
|
||||
else:
|
||||
view = SimpleMenu(pages)
|
||||
view.remove_item(view.last_button)
|
||||
view.remove_item(view.first_button)
|
||||
has_next = False
|
||||
has_prev = False
|
||||
has_close = False
|
||||
to_add = {}
|
||||
for emoji, func in controls.items():
|
||||
if func == next_page:
|
||||
has_next = True
|
||||
if emoji != view.forward_button.emoji:
|
||||
view.forward_button.emoji = discord.PartialEmoji.from_str(emoji)
|
||||
elif func == prev_page:
|
||||
has_prev = True
|
||||
if emoji != view.backward_button.emoji:
|
||||
view.backward_button.emoji = discord.PartialEmoji.from_str(emoji)
|
||||
elif func == close_menu:
|
||||
has_close = True
|
||||
else:
|
||||
to_add[emoji] = func
|
||||
if not has_next:
|
||||
view.remove_item(view.forward_button)
|
||||
if not has_prev:
|
||||
view.remove_item(view.backward_button)
|
||||
if not has_close:
|
||||
view.remove_item(view.stop_button)
|
||||
for emoji, func in to_add.items():
|
||||
view.add_item(_GenericButton(emoji, func))
|
||||
await view.start(ctx)
|
||||
_active_menus[view.message.id] = view
|
||||
await view.wait()
|
||||
del _active_menus[view.message.id]
|
||||
return
|
||||
current_page = pages[page]
|
||||
|
||||
if not message:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user