Skip to content

Commit 0517d2e

Browse files
authored
button priority
1 parent 93fb09a commit 0517d2e

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-3
lines changed

discord/ui/action_row.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ def _add_component_from_item(self, item: Item):
9191

9292
def _set_components(self, items: list[Item]):
9393
self._underlying.components.clear()
94+
if not any(isinstance(b, Select) for b in self.children):
95+
a, b = [], []
96+
for i in items:
97+
if i.priority is None:
98+
b.append(i)
99+
else:
100+
a.append(i)
101+
items = sorted(a, key=lambda b: b.priority) + b
94102
for item in items:
95103
self._add_component_from_item(item)
96104

@@ -108,8 +116,12 @@ def add_item(self, item: Item) -> Self:
108116
An :class:`Item` was not passed.
109117
"""
110118

111-
if not isinstance(item, Item):
112-
raise TypeError(f"expected Item not {item.__class__!r}")
119+
if not isinstance(item, (Select, Button)):
120+
raise TypeError(f"expected Select or Button, not {item.__class__!r}")
121+
if item.row:
122+
raise ValueError(f"{item.__class__!r}.row is not supported in ActionRow")
123+
if self.width + item.width > 5:
124+
raise ValueError(f"Not enough space left on this ActionRow")
113125

114126
item._view = self.view
115127
item.parent = self
@@ -166,6 +178,7 @@ def add_button(
166178
emoji: str | GuildEmoji | AppEmoji | PartialEmoji | None = None,
167179
sku_id: int | None = None,
168180
id: int | None = None,
181+
priority: int | None = None
169182
) -> Self:
170183
"""Adds a :class:`Button` to the action row.
171184
@@ -191,6 +204,10 @@ def add_button(
191204
The ID of the SKU this button refers to.
192205
id: Optional[:class:`int`]
193206
The button's ID.
207+
priority: Optional[:class:`int`]
208+
An integer greater than 0. If specified, decides the position
209+
of the button in this row instead of going by order of addition. The lower this number, the earlier its position.
210+
This ActionRow's children will be reordered when the View containing it is sent. A priority of ``None`` will be ordered after any specified priority.
194211
"""
195212

196213
button = Button(
@@ -202,6 +219,7 @@ def add_button(
202219
emoji=emoji,
203220
sku_id=sku_id,
204221
id=id,
222+
priority=priority
205223
)
206224

207225
return self.add_item(button)
@@ -320,6 +338,14 @@ def enable_all_items(self, *, exclusions: list[Item] | None = None) -> Self:
320338
item.disabled = False
321339
return self
322340

341+
@property
342+
def width(self):
343+
"""Return the sum of the items' widths."""
344+
t = 0
345+
for item in self.children:
346+
t += 1 if item._underlying.type is ComponentType.button else 5
347+
return t
348+
323349
def walk_items(self) -> Iterator[Item]:
324350
yield from self.children
325351

discord/ui/button.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,14 @@ class Button(Item[V]):
7878
7979
.. warning::
8080
81-
This parameter does not work with V2 components or with more than 25 items in your view.
81+
This parameter does not work in :class:`ActionRow`.
8282
8383
id: Optional[:class:`int`]
8484
The button's ID.
85+
priority: Optional[:class:`int`]
86+
Only works in :class:`ActionRow`. Any integer greater than 0. If specified, decides the position
87+
of the button in this row instead of going by order of addition. The lower this number, the earlier its position.
88+
The ActionRow's children will be reordered when the View containing this button is sent.
8589
"""
8690

8791
__item_repr_attributes__: tuple[str, ...] = (
@@ -94,6 +98,7 @@ class Button(Item[V]):
9498
"row",
9599
"custom_id",
96100
"id",
101+
"priority",
97102
)
98103

99104
def __init__(
@@ -108,6 +113,7 @@ def __init__(
108113
sku_id: int | None = None,
109114
row: int | None = None,
110115
id: int | None = None,
116+
priority: int | None = None,
111117
):
112118
super().__init__()
113119
if label and len(str(label)) > 80:
@@ -120,11 +126,14 @@ def __init__(
120126
raise TypeError("cannot mix both url and sku_id with Button")
121127
if custom_id is not None and sku_id is not None:
122128
raise TypeError("cannot mix both sku_id and custom_id with Button")
129+
if priority and (priority < 0 or not isinstance(priority, int)):
130+
raise ValueError("priority must be an integer greater than 0")
123131

124132
if not isinstance(custom_id, str) and custom_id is not None:
125133
raise TypeError(
126134
f"expected custom_id to be str, not {custom_id.__class__.__name__}"
127135
)
136+
self.priority: int | None = priority
128137

129138
self._provided_custom_id = custom_id is not None
130139
if url is None and custom_id is None and sku_id is None:
@@ -294,6 +303,7 @@ def button(
294303
emoji: str | GuildEmoji | AppEmoji | PartialEmoji | None = None,
295304
row: int | None = None,
296305
id: int | None = None,
306+
priority: int | None = None,
297307
) -> Callable[[ItemCallbackType[Button[V]]], Button[V]]:
298308
"""A decorator that attaches a button to a component.
299309
@@ -328,6 +338,15 @@ def button(
328338
like to control the relative positioning of the row then passing an index is advised.
329339
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
330340
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
341+
342+
.. warning::
343+
344+
This parameter does not work in :class:`ActionRow`.
345+
346+
priority: Optional[:class:`int`]
347+
Only works in :class:`ActionRow`. Any integer greater than 0. If specified, decides the position
348+
of the button in this row instead of going by order of addition. The lower this number, the earlier its position.
349+
The ActionRow's children will be reordered when the View containing it is sent. A priority of ``None`` will be ordered after any specified priority.
331350
"""
332351

333352
def decorator(func: ItemCallbackType) -> ItemCallbackType:
@@ -344,6 +363,7 @@ def decorator(func: ItemCallbackType) -> ItemCallbackType:
344363
"emoji": emoji,
345364
"row": row,
346365
"id": id,
366+
"priority": priority
347367
}
348368
return func
349369

discord/ui/container.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ class Container(Item[V]):
6565
"id",
6666
)
6767

68+
def __init_subclass__(cls) -> None:
69+
for base in reversed(cls.__mro__):
70+
for member in base.__dict__.values():
71+
if hasattr(member, "__discord_ui_model_type__"):
72+
raise ValueError("The @button and @select decorators are incompatible with Container. Use ActionRow instead.")
73+
6874
def __init__(
6975
self,
7076
*items: Item,

discord/ui/view.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,12 @@ class DesignerView(BaseView):
782782

783783
MAX_ITEMS: int = 40
784784

785+
def __init_subclass__(cls) -> None:
786+
for base in reversed(cls.__mro__):
787+
for member in base.__dict__.values():
788+
if hasattr(member, "__discord_ui_model_type__"):
789+
raise ValueError("The @button and @select decorators are incompatible with DesignerView. Use ActionRow instead.")
790+
785791
def __init__(
786792
self,
787793
*items: Item[V],

0 commit comments

Comments
 (0)