6
6
import logging
7
7
import re
8
8
from collections import OrderedDict , namedtuple
9
- from typing import List , Optional
9
+ from typing import List , Union
10
10
11
11
# Needed for the setup.py script
12
12
__version__ = '1.0.0'
@@ -281,7 +281,7 @@ def get_buttons(cls):
281
281
return buttons
282
282
283
283
284
- class Menu (nextcord . ui . View , metaclass = _MenuMeta ):
284
+ class Menu (metaclass = _MenuMeta ):
285
285
r"""An interface that allows handling menus by using reactions as buttons.
286
286
287
287
Buttons should be marked with the :func:`button` decorator. Please note that
@@ -316,7 +316,6 @@ class Menu(nextcord.ui.View, metaclass=_MenuMeta):
316
316
def __init__ (self , * , timeout = DEFAULT_TIMEOUT , delete_message_after = False ,
317
317
clear_reactions_after = False , check_embeds = False , message = None ):
318
318
319
- super ().__init__ (timeout = timeout )
320
319
self .timeout = timeout
321
320
self .delete_message_after = delete_message_after
322
321
self .clear_reactions_after = clear_reactions_after
@@ -757,6 +756,25 @@ async def send_initial_message(self, ctx, channel):
757
756
"""
758
757
raise NotImplementedError
759
758
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
+
760
778
async def _set_all_disabled (self , disable : bool ):
761
779
"""|coro|
762
780
@@ -786,11 +804,11 @@ async def disable(self):
786
804
await self ._set_all_disabled (True )
787
805
788
806
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 )
794
812
795
813
796
814
class PageSource :
@@ -914,7 +932,7 @@ async def format_page(self, menu, page):
914
932
raise NotImplementedError
915
933
916
934
917
- class MenuPagesBase (Menu ):
935
+ class MenuPagesBase (ButtonMenu ):
918
936
"""A base class dedicated to pagination for reaction and button menus.
919
937
920
938
Attributes
@@ -927,7 +945,7 @@ class MenuPagesBase(Menu):
927
945
PREVIOUS_PAGE = '\N{BLACK LEFT-POINTING TRIANGLE} \ufe0f '
928
946
NEXT_PAGE = '\N{BLACK RIGHT-POINTING TRIANGLE} \ufe0f '
929
947
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 '
931
949
932
950
def __init__ (self , source , ** kwargs ):
933
951
self ._source = source
@@ -1065,8 +1083,8 @@ class MenuPaginationButton(nextcord.ui.Button['MenuPaginationButton']):
1065
1083
"""
1066
1084
A custom button for pagination that will be disabled when unavailable.
1067
1085
"""
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 )
1070
1088
self ._emoji = _cast_emoji (emoji )
1071
1089
1072
1090
async def callback (self , interaction : nextcord .Interaction ):
@@ -1075,7 +1093,6 @@ async def callback(self, interaction: nextcord.Interaction):
1075
1093
"""
1076
1094
assert self .view is not None
1077
1095
view : ButtonMenuPages = self .view
1078
- stopped = False
1079
1096
1080
1097
# change the current page
1081
1098
if self ._emoji .name == view .FIRST_PAGE :
@@ -1086,57 +1103,52 @@ async def callback(self, interaction: nextcord.Interaction):
1086
1103
await view .show_checked_page (view .current_page + 1 )
1087
1104
elif self ._emoji .name == view .LAST_PAGE :
1088
1105
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 ()
1092
1109
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 ()
1095
1114
1096
1115
# update the view
1097
1116
await interaction .response .edit_message (view = view )
1098
1117
1099
1118
1100
- class ButtonMenuPages (MenuPagesBase , nextcord . ui . View ):
1119
+ class ButtonMenuPages (MenuPagesBase ):
1101
1120
"""A special type of Menu dedicated to pagination with button components.
1102
1121
1103
1122
Parameters
1104
1123
-----------
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.
1108
1126
1109
1127
Attributes
1110
1128
------------
1111
1129
current_page: :class:`int`
1112
1130
The current page that we are in. Zero-indexed
1113
1131
between [0, :attr:`PageSource.max_pages`).
1114
1132
"""
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 ))
1126
1140
self ._disable_unavailable_buttons ()
1127
1141
1128
- def _disable_unavailable_buttons (self , stopped : bool = False ):
1142
+ def _disable_unavailable_buttons (self ):
1129
1143
"""
1130
1144
Disables buttons that are unavailable to be pressed.
1131
1145
"""
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
1140
1152
1141
1153
1142
1154
0 commit comments