From cc1cd251797bcbff2ac5ddf3cdcaa2d6a95cbe7f Mon Sep 17 00:00:00 2001 From: normal Date: Sat, 22 Jun 2024 03:40:34 +0300 Subject: [PATCH 1/4] Added functionality for negative numbers. --- aiogram/utils/keyboard.py | 60 ++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/aiogram/utils/keyboard.py b/aiogram/utils/keyboard.py index 921c28c1..841a7b6d 100644 --- a/aiogram/utils/keyboard.py +++ b/aiogram/utils/keyboard.py @@ -207,12 +207,44 @@ class KeyboardBuilder(Generic[ButtonType], ABC): ) return self + def _markup_constructor( + self, + sizes: list, + buttons: list, + repeat: bool + ) -> list: + """ + Constructs the keyboard according to the specified parameters. + + :param sizes: + :param buttons: + :param repeat: + :return: + """ + markup = [] + row: List[ButtonType] = [] + validated_sizes = map(self._validate_size, sizes) + sizes_iter = repeat_all(validated_sizes) if repeat else repeat_last(validated_sizes) + size = next(sizes_iter) + + for button in buttons: + if len(row) >= size: + markup.append(row) + size = next(sizes_iter) + row = [] + row.append(button) + + if row: + markup.append(row) + return markup + def adjust(self, *sizes: int, repeat: bool = False) -> "KeyboardBuilder[ButtonType]": """ Adjust previously added buttons to specific row sizes. By default, when the sum of passed sizes is lower than buttons count the last one size will be used for tail of the markup. + If size is negative - it will be used for the last buttons. Negative values are more important than positive ones. If repeat=True is passed - all sizes will be cycled when available more buttons count than all sizes @@ -220,23 +252,19 @@ class KeyboardBuilder(Generic[ButtonType], ABC): :param repeat: :return: """ - if not sizes: - sizes = (self.max_width,) - - validated_sizes = map(self._validate_size, sizes) - sizes_iter = repeat_all(validated_sizes) if repeat else repeat_last(validated_sizes) - size = next(sizes_iter) - + if sizes: + negative_sizes = [-size for size in sizes if size < 0] + positive_sizes = [size for size in sizes if size >= 0] if len(negative_sizes) != len(sizes) else (self.max_width,) + else: + positive_sizes = (self.max_width,) + negative_sizes = [] markup = [] - row: List[ButtonType] = [] - for button in self.buttons: - if len(row) >= size: - markup.append(row) - size = next(sizes_iter) - row = [] - row.append(button) - if row: - markup.append(row) + buttons = list(self.buttons) + + if positive_sizes: + markup.extend(self._markup_constructor(positive_sizes, buttons[:-sum(negative_sizes)] if negative_sizes else buttons, repeat)) + if negative_sizes: + markup.extend(self._markup_constructor(negative_sizes, buttons[-sum(negative_sizes):], repeat)) self._markup = markup return self From 45a87a8dc3a9c1c38f2ee991b025aefd357cbd36 Mon Sep 17 00:00:00 2001 From: normal Date: Sat, 22 Jun 2024 05:36:17 +0300 Subject: [PATCH 2/4] Code-style fix --- aiogram/utils/keyboard.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/aiogram/utils/keyboard.py b/aiogram/utils/keyboard.py index 841a7b6d..14aa144c 100644 --- a/aiogram/utils/keyboard.py +++ b/aiogram/utils/keyboard.py @@ -207,12 +207,7 @@ class KeyboardBuilder(Generic[ButtonType], ABC): ) return self - def _markup_constructor( - self, - sizes: list, - buttons: list, - repeat: bool - ) -> list: + def _markup_constructor(self, sizes: list, buttons: list, repeat: bool) -> list: """ Constructs the keyboard according to the specified parameters. @@ -254,7 +249,11 @@ class KeyboardBuilder(Generic[ButtonType], ABC): """ if sizes: negative_sizes = [-size for size in sizes if size < 0] - positive_sizes = [size for size in sizes if size >= 0] if len(negative_sizes) != len(sizes) else (self.max_width,) + positive_sizes = ( + [size for size in sizes if size >= 0] + if len(negative_sizes) != len(sizes) + else (self.max_width,) + ) else: positive_sizes = (self.max_width,) negative_sizes = [] @@ -262,9 +261,17 @@ class KeyboardBuilder(Generic[ButtonType], ABC): buttons = list(self.buttons) if positive_sizes: - markup.extend(self._markup_constructor(positive_sizes, buttons[:-sum(negative_sizes)] if negative_sizes else buttons, repeat)) + markup.extend( + self._markup_constructor( + positive_sizes, + buttons[: -sum(negative_sizes)] if negative_sizes else buttons, + repeat, + ) + ) if negative_sizes: - markup.extend(self._markup_constructor(negative_sizes, buttons[-sum(negative_sizes):], repeat)) + markup.extend( + self._markup_constructor(negative_sizes, buttons[-sum(negative_sizes) :], repeat) + ) self._markup = markup return self From c1a737cd73de46aa73a6036ba3c733907360438b Mon Sep 17 00:00:00 2001 From: normal Date: Sat, 22 Jun 2024 05:36:48 +0300 Subject: [PATCH 3/4] Tests added --- tests/test_utils/test_keyboard.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_utils/test_keyboard.py b/tests/test_utils/test_keyboard.py index 27aeb1ac..d2354d3e 100644 --- a/tests/test_utils/test_keyboard.py +++ b/tests/test_utils/test_keyboard.py @@ -183,11 +183,17 @@ class TestKeyboardBuilder: [ [0, False, [], []], [0, False, [2], []], + [0, False, [-2], []], [1, False, [2], [1]], + [1, False, [-2], [1]], [3, False, [2], [2, 1]], + [3, False, [-2], [1, 2]], [10, False, [3, 2, 1], [3, 2, 1, 1, 1, 1, 1]], + [10, False, [3, 2, -1], [3, 2, 2, 2, 1]], + [10, False, [3, -2, 1], [3, 1, 1, 1, 1, 1, 2]], [12, False, [], [10, 2]], [12, True, [3, 2, 1], [3, 2, 1, 3, 2, 1]], + [12, True, [3, -1, -4], [3, 3, 1, 1, 4]], ], ) def test_adjust(self, count, repeat, sizes, shape): From e630c2a85e8e91113a21835ad1bdf3c6a20cb092 Mon Sep 17 00:00:00 2001 From: normal Date: Sat, 22 Jun 2024 06:12:17 +0300 Subject: [PATCH 4/4] Changelog added --- CHANGES/1522.feature.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 CHANGES/1522.feature.rst diff --git a/CHANGES/1522.feature.rst b/CHANGES/1522.feature.rst new file mode 100644 index 00000000..a593173d --- /dev/null +++ b/CHANGES/1522.feature.rst @@ -0,0 +1,28 @@ +==== +Added functionality for negative numbers in keyboard adjust. +==== +If the function gets a negative number in `adjust()`, it applies them to the lower buttons in the same way that positive numbers affect the upper buttons. + +Negative numbers have a higher priority in formatting. + +You can make that: + +.. image:: https://github.com/aiogram/aiogram/assets/104172267/4d55a4c6-23fa-4a73-824b-0f2d7c879431 + + +With this code: + + .. code-block:: python + + def test_markup(): + builder = ReplyKeyboardBuilder() + builder.row(*(KeyboardButton(text=f"test-{index}") for index in range(6))) + builder.button(text="Add", callback_data="add") + builder.button(text="Cancel", callback_data="cancel") + builder.adjust(3, -1, -1) + return builder.as_markup() + + @router.message(Command('test')) + async def test_handler(msg: Message) -> None: + await msg.answer("This is TEST!!!", reply_markup=test_markup()) + ...