66import logging
77import re
88from collections import OrderedDict , namedtuple
9- from typing import List , Optional
9+ from typing import List , Union
1010
1111# Needed for the setup.py script
1212__version__ = '1.0.0'
@@ -281,7 +281,7 @@ def get_buttons(cls):
281281 return buttons
282282
283283
284- class Menu (nextcord . ui . View , metaclass = _MenuMeta ):
284+ class Menu (metaclass = _MenuMeta ):
285285 r"""An interface that allows handling menus by using reactions as buttons.
286286
287287 Buttons should be marked with the :func:`button` decorator. Please note that
@@ -316,7 +316,6 @@ class Menu(nextcord.ui.View, metaclass=_MenuMeta):
316316 def __init__ (self , * , timeout = DEFAULT_TIMEOUT , delete_message_after = False ,
317317 clear_reactions_after = False , check_embeds = False , message = None ):
318318
319- super ().__init__ (timeout = timeout )
320319 self .timeout = timeout
321320 self .delete_message_after = delete_message_after
322321 self .clear_reactions_after = clear_reactions_after
@@ -757,6 +756,25 @@ async def send_initial_message(self, ctx, channel):
757756 """
758757 raise NotImplementedError
759758
759+ def stop (self ):
760+ """Stops the internal loop."""
761+ self ._running = False
762+ for task in self .__tasks :
763+ task .cancel ()
764+ self .__tasks .clear ()
765+
766+ class ButtonMenu (Menu , nextcord .ui .View ):
767+ r"""An interface that allows handling menus by using button interaction components.
768+
769+ Buttons should be marked with the :func:`nextcord.ui.button` decorator. Please note that
770+ this expects the methods to have two parameters, the ``button`` and the ``interaction``.
771+ The ``button`` is of type :class:`nextcord.ui.Button`.
772+ The ``interaction`` is of type :class:`nextcord.Interaction`.
773+ """
774+ def __init__ (self , timeout = DEFAULT_TIMEOUT , * args , ** kwargs ):
775+ Menu .__init__ (self , timeout = timeout , * args , ** kwargs )
776+ nextcord .ui .View .__init__ (self , timeout = timeout )
777+
760778 async def _set_all_disabled (self , disable : bool ):
761779 """|coro|
762780
@@ -786,11 +804,11 @@ async def disable(self):
786804 await self ._set_all_disabled (True )
787805
788806 def stop (self ):
789- """Stops the internal loop."""
790- self . _running = False
791- for task in self . __tasks :
792- task . cancel ()
793- self . __tasks . clear ( )
807+ """Stops the internal loop and view interactions ."""
808+ # stop the menu loop
809+ Menu . stop ( self )
810+ # stop view interactions
811+ nextcord . ui . View . stop ( self )
794812
795813
796814class PageSource :
@@ -914,7 +932,7 @@ async def format_page(self, menu, page):
914932 raise NotImplementedError
915933
916934
917- class MenuPagesBase (Menu ):
935+ class MenuPagesBase (ButtonMenu ):
918936 """A base class dedicated to pagination for reaction and button menus.
919937
920938 Attributes
@@ -927,7 +945,7 @@ class MenuPagesBase(Menu):
927945 PREVIOUS_PAGE = '\N{BLACK LEFT-POINTING TRIANGLE} \ufe0f '
928946 NEXT_PAGE = '\N{BLACK RIGHT-POINTING TRIANGLE} \ufe0f '
929947 LAST_PAGE = '\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR} \ufe0f '
930- STOP_PAGINATION = '\N{BLACK SQUARE FOR STOP} \ufe0f '
948+ STOP = '\N{BLACK SQUARE FOR STOP} \ufe0f '
931949
932950 def __init__ (self , source , ** kwargs ):
933951 self ._source = source
@@ -1065,8 +1083,8 @@ class MenuPaginationButton(nextcord.ui.Button['MenuPaginationButton']):
10651083 """
10661084 A custom button for pagination that will be disabled when unavailable.
10671085 """
1068- def __init__ (self , emoji ):
1069- super ().__init__ (style = nextcord . ButtonStyle . primary , emoji = emoji )
1086+ def __init__ (self , style : nextcord . ButtonStyle , emoji : Union [ str , nextcord . Emoji , nextcord . PartialEmoji ] ):
1087+ super ().__init__ (style = style , emoji = emoji )
10701088 self ._emoji = _cast_emoji (emoji )
10711089
10721090 async def callback (self , interaction : nextcord .Interaction ):
@@ -1075,7 +1093,6 @@ async def callback(self, interaction: nextcord.Interaction):
10751093 """
10761094 assert self .view is not None
10771095 view : ButtonMenuPages = self .view
1078- stopped = False
10791096
10801097 # change the current page
10811098 if self ._emoji .name == view .FIRST_PAGE :
@@ -1086,57 +1103,52 @@ async def callback(self, interaction: nextcord.Interaction):
10861103 await view .show_checked_page (view .current_page + 1 )
10871104 elif self ._emoji .name == view .LAST_PAGE :
10881105 await view .show_page (view ._source .get_max_pages () - 1 )
1089- elif self . _emoji . name == view . STOP_PAGINATION :
1090- nextcord . ui . View . stop ( view )
1091- stopped = True
1106+
1107+ # disable buttons that are unavailable
1108+ view . _disable_unavailable_buttons ()
10921109
1093- # disable the buttons that are unavailable
1094- view ._disable_unavailable_buttons (stopped )
1110+ # disable all buttons if stop is pressed
1111+ if self ._emoji .name == view .STOP :
1112+ await view .disable ()
1113+ view .stop ()
10951114
10961115 # update the view
10971116 await interaction .response .edit_message (view = view )
10981117
10991118
1100- class ButtonMenuPages (MenuPagesBase , nextcord . ui . View ):
1119+ class ButtonMenuPages (MenuPagesBase ):
11011120 """A special type of Menu dedicated to pagination with button components.
11021121
11031122 Parameters
11041123 -----------
1105- timeout: Optional[:class:`float`]
1106- Timeout in seconds from last interaction with the UI before no longer accepting input.
1107- If ``None`` then there is no timeout.
1124+ style: :class:`nextcord.ui.ButtonStyle`
1125+ The button style to use for the pagination buttons.
11081126
11091127 Attributes
11101128 ------------
11111129 current_page: :class:`int`
11121130 The current page that we are in. Zero-indexed
11131131 between [0, :attr:`PageSource.max_pages`).
11141132 """
1115- def __init__ (self , source , timeout = DEFAULT_TIMEOUT , ** kwargs ):
1116- MenuPagesBase .__init__ (self , source , ** kwargs )
1117- nextcord .ui .View .__init__ (self , timeout = timeout )
1118- skip_double_triangle_buttons = self ._skip_double_triangle_buttons ()
1119- if not skip_double_triangle_buttons :
1120- nextcord .ui .View .add_item (self , MenuPaginationButton (self .FIRST_PAGE ))
1121- nextcord .ui .View .add_item (self , MenuPaginationButton (self .PREVIOUS_PAGE ))
1122- nextcord .ui .View .add_item (self , MenuPaginationButton (self .NEXT_PAGE ))
1123- if not skip_double_triangle_buttons :
1124- nextcord .ui .View .add_item (self , MenuPaginationButton (self .LAST_PAGE ))
1125- nextcord .ui .View .add_item (self , MenuPaginationButton (self .STOP_PAGINATION ))
1133+ def __init__ (self , source : PageSource , style : nextcord .ButtonStyle = nextcord .ButtonStyle .primary , ** kwargs ):
1134+ super ().__init__ (source , ** kwargs )
1135+ # add buttons to the view
1136+ for emoji in (self .FIRST_PAGE , self .PREVIOUS_PAGE , self .NEXT_PAGE , self .LAST_PAGE , self .STOP ):
1137+ if emoji in (self .FIRST_PAGE , self .LAST_PAGE ) and self ._skip_double_triangle_buttons ():
1138+ continue
1139+ self .add_item (MenuPaginationButton (style = style , emoji = emoji ))
11261140 self ._disable_unavailable_buttons ()
11271141
1128- def _disable_unavailable_buttons (self , stopped : bool = False ):
1142+ def _disable_unavailable_buttons (self ):
11291143 """
11301144 Disables buttons that are unavailable to be pressed.
11311145 """
1132- children : List [MenuPaginationButton ] = self .children
1133- for child in children :
1134- if stopped :
1135- child .disabled = True
1136- elif child .emoji .name in (self .FIRST_PAGE , self .PREVIOUS_PAGE ):
1137- child .disabled = self .current_page == 0
1138- elif child .emoji .name in (self .LAST_PAGE , self .NEXT_PAGE ):
1139- child .disabled = self .current_page == self ._source .get_max_pages () - 1
1146+ buttons : List [MenuPaginationButton ] = self .children
1147+ for button in buttons :
1148+ if button .emoji .name in (self .FIRST_PAGE , self .PREVIOUS_PAGE ):
1149+ button .disabled = self .current_page == 0
1150+ elif button .emoji .name in (self .LAST_PAGE , self .NEXT_PAGE ):
1151+ button .disabled = self .current_page == self ._source .get_max_pages () - 1
11401152
11411153
11421154
0 commit comments