Files
ShiteMusicBot/musicbot/utils/userinteraction/selector.py
T

124 lines
4.8 KiB
Python

from __future__ import annotations
# Discord Packages
import discord
from enum import Enum, auto
from typing import Callable, Coroutine, List, Optional
from .paginators import TextPaginator
from .scroller import ClearOn, Scroller
class SelectMode(Enum):
SingleSelect = auto()
MultiSelect = auto()
SpanningMultiSelect = auto() # Used to keep the view around after select
class SelectorButton(discord.ui.Button):
def __init__(self, label, callback: Callable[[discord.Interaction, SelectorButton], Coroutine],
style=discord.ButtonStyle.gray, **kwargs):
super().__init__(label=label, style=style, **kwargs)
# The callback we need to supply to disord.py takes only a discord.Interaction
# we want to have access to the button that was clicked (self)
# so we can update or modify it upon it being pressed i.e. change color.
def add_button_to_callback(func):
async def wrapped_callback(interaction: discord.Interaction):
await func(interaction, self)
return wrapped_callback
self.callback = add_button_to_callback(callback)
class SelectorItem:
def __init__(self, identifier, button_label, callback: Callable[[discord.Interaction, SelectorButton], Coroutine]):
self.button_label = button_label
self.identifier = identifier
self.callback = callback
class Selector(TextPaginator, Scroller):
def __init__(self, ctx, choices: List[SelectorItem], select_mode: SelectMode,
use_tick_for_stop_emoji: bool = False, max_size=2000, default_text=" ", **embed_base):
self.selector_mode: SelectMode = select_mode
self.selections_per_page = 5
self.selections = choices
TextPaginator.__init__(self, max_size=max_size, max_lines=self.selections_per_page, **embed_base)
for selection in self.selections:
if (selection.identifier):
self.add_line(selection.identifier)
if len(self.pages) == 0:
self.add_line(default_text)
self.close_page()
Scroller.__init__(self, ctx, self, use_tick_for_stop_emoji=use_tick_for_stop_emoji,
show_cancel_for_single_page=True)
# Contains a list of all buttons that can be interacted with, both
# currently visible and not.
self.buttons = []
# The list of currently visible buttons, gets cleared upon scroll.
self.currently_visible_buttons = []
# Depending on the selector we might want the result of calculations in
# the callbacks after we have finished interacting with the scroller
self.callback_results = []
# Wrap the provided so that we can both update the view if needed
# or terminate the selection process upon interaction
def with_update_view(func):
async def wrapped_callback(interaction: discord.Interaction, button: SelectorButton):
if (interaction.user.id != ctx.author.id):
return await interaction.response.defer()
result = await func(interaction, button)
self.callback_results.append(result)
self.update_view_on_interaction(interaction)
await interaction.response.defer()
match self.selector_mode:
case SelectMode.SingleSelect:
await self.stop(was_timeout=False, clear_scroller_view=True)
case SelectMode.SpanningMultiSelect:
await self.stop(was_timeout=False, clear_scroller_view=False)
case _:
await self.update_message()
return wrapped_callback
for choice in self.selections:
self.buttons.append(SelectorButton(choice.button_label, with_update_view(choice.callback), row=0))
def build_view(self):
super().build_view()
self.update_buttons()
def update_view(self):
super().update_view()
self.update_buttons()
def update_buttons(self):
for button in self.currently_visible_buttons:
self.view.remove_item(item=button)
self.currently_visible_buttons = []
self.page_number
start = self.page_number * self.selections_per_page
end = min((self.page_number + 1) * self.selections_per_page, len(self.selections))
for button in self.buttons[start:end]:
self.view.add_item(item=button)
self.currently_visible_buttons.append(button)
async def start_scrolling(self, clear_mode: ClearOn = ClearOn.Timeout,
message: Optional[discord.Message] = None,
start_page: int = 0):
message, timed_out = await super().start_scrolling(clear_mode, message, start_page)
return message, timed_out, self.callback_results