diff --git a/src/tagstudio/qt/controllers/main_window.py b/src/tagstudio/qt/controllers/main_window.py index 1f4dcc84..ccd58b07 100644 --- a/src/tagstudio/qt/controllers/main_window.py +++ b/src/tagstudio/qt/controllers/main_window.py @@ -41,9 +41,9 @@ from tagstudio.i18n.platform_strings import trash_term from tagstudio.i18n.translations import Translations from tagstudio.qt.controllers.banner import Banner from tagstudio.qt.controllers.inspector import Inspector +from tagstudio.qt.controllers.pagination import Pagination from tagstudio.qt.helpers.mnemonics import assign_mnemonics from tagstudio.qt.mixed.landing import LandingWidget -from tagstudio.qt.mixed.pagination import Pagination from tagstudio.qt.resource_manager import ResourceManager from tagstudio.qt.views.layouts.thumb_grid_layout import ThumbGridLayout from tagstudio.qt.views.styles.color_overlay import auto_theme_overlay diff --git a/src/tagstudio/qt/controllers/pagination.py b/src/tagstudio/qt/controllers/pagination.py new file mode 100644 index 00000000..7f46f5d5 --- /dev/null +++ b/src/tagstudio/qt/controllers/pagination.py @@ -0,0 +1,210 @@ +# SPDX-FileCopyrightText: (c) TagStudio Contributors +# SPDX-License-Identifier: GPL-3.0-only + + +from typing import override +from warnings import catch_warnings + +from PySide6.QtCore import QEvent, QObject, Qt, Signal +from PySide6.QtWidgets import QHBoxLayout, QPushButton, QWidget + +from tagstudio.qt.views.pagination_view import PaginationView +from tagstudio.qt.views.styles.stylesheets import pagination_style + + +class Pagination(QWidget): + HEIGHT = 36 + _RIGHT_MARGIN = 6 + _SCROLLBAR_WIDTH = 14 + + index = Signal(int) + + def __init__(self, parent: QWidget | None = None) -> None: + super().__init__(parent) + self.page_count: int = 0 + self.current_page_index: int = 0 + + self.setHidden(True) + self.setObjectName("pagination") + self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground) + self.setFixedHeight(self.HEIGHT) + self.setStyleSheet(pagination_style()) + + self.view = PaginationView() + self.setLayout(self.view) + self._connect_callbacks() + + if parent is not None: + parent.installEventFilter(self) + self._sync_geometry() + + def _connect_callbacks(self) -> None: + self.view.current_page_field.returnPressed.connect( + lambda: self._goto_page(int(self.view.current_page_field.text()) - 1) + ) + + def update_buttons(self, page_count: int, index: int, emit: bool = True): + if index < 0: + raise ValueError("Negative index detected") + + view = self.view + for i in range(0, 10): + if button := self._get_button_at(view.start_buffer_layout, i): + button.setHidden(True) + if button := self._get_button_at(view.end_buffer_layout, i): + button.setHidden(True) + + end_page = page_count - 1 + # Hide everything if there are only one or less pages. + if page_count <= 1: + self.setHidden(True) + + # Enable/Disable Next + Prev Buttons + elif page_count > 1: + if index == 0: + view.prev_button.setDisabled(True) + else: + self._assign_click(view.prev_button, index - 1) + view.prev_button.setDisabled(False) + + if index == end_page: + view.next_button.setDisabled(True) + else: + self._assign_click(view.next_button, index + 1) + view.next_button.setDisabled(False) + + # Set Ellipses Sizes + if 8 <= page_count <= 11: + end_scale = max(1, page_count - index - 6) + start_scale = max(1, index - 5) + elif page_count > 11: + end_scale = max(1, 7 - index) + start_scale = max(1, (7 - (end_page - index))) + else: + end_scale, start_scale = 1, 1 + + if page_count >= 8: + end_size = view.BUTTON_SIZE.width() * end_scale + (3 * (end_scale - 1)) + start_size = view.BUTTON_SIZE.width() * start_scale + (3 * (start_scale - 1)) + view.end_ellipses.setMinimumWidth(end_size) + view.end_ellipses.setMaximumWidth(end_size) + view.start_ellipses.setMinimumWidth(start_size) + view.start_ellipses.setMaximumWidth(start_size) + + # Enable/Disable Ellipses + if index <= view.BUFFER_PAGE_COUNT + 1: + view.start_ellipses.setHidden(True) + else: + view.start_ellipses.setHidden(False) + self._assign_click(view.start_button, 0) + if index >= (page_count - view.BUFFER_PAGE_COUNT - 2): + view.end_ellipses.setHidden(True) + else: + view.end_ellipses.setHidden(False) + + # Hide/Unhide Start + End Buttons + if index != 0: + view.start_button.setText("1") + self._assign_click(view.start_button, 0) + view.start_button.setHidden(False) + else: + view.start_button.setHidden(True) + if index != page_count - 1: + view.end_button.setText(str(page_count)) + self._assign_click(view.end_button, page_count - 1) + view.end_button.setHidden(False) + else: + view.end_button.setHidden(True) + + if index == 0 or index == 1: + view.start_buffer_container.setHidden(True) + else: + view.start_buffer_container.setHidden(False) + + if index == page_count - 1 or index == page_count - 2: + view.end_buffer_container.setHidden(True) + else: + view.end_buffer_container.setHidden(False) + + # Current Field and Buffer Pages + for i in range(0, page_count): + # Set Field + if i == index: + if button := self._get_button_at(view.start_buffer_layout, i): + button.setHidden(True) + if button := self._get_button_at(view.end_buffer_layout, i): + button.setHidden(True) + view.current_page_field.setText(str(i + 1)) + + start_offset = max(0, (index - 4) - 4) + end_offset = min(page_count - 1, (index + 4) - 4) + if i < index: + if (i != 0) and i >= index - 4: + if button := self._get_button_at( + view.start_buffer_layout, i - start_offset + ): + button.setHidden(False) + button.setText(str(i + 1)) + self._assign_click(button, i) + else: + if button := self._get_button_at(view.start_buffer_layout, i): + button.setHidden(True) + if button := self._get_button_at(view.end_buffer_layout, i): + button.setHidden(True) + elif i > index: + if i != page_count - 1 and i <= index + 4: + if button := self._get_button_at(view.end_buffer_layout, i - end_offset): + button.setHidden(False) + button.setText(str(i + 1)) + self._assign_click(button, i) + else: + if button := self._get_button_at(view.end_buffer_layout, i): + button.setHidden(True) + for j in range(0, view.BUFFER_PAGE_COUNT): + if button := self._get_button_at( + view.end_buffer_layout, i - end_offset + j + ): + button.setHidden(True) + + if button := self._get_button_at(view.start_buffer_layout, i - 1): + button.setHidden(True) + + self.setHidden(False) + self._sync_geometry() + self.raise_() + + view.validator.setTop(page_count) + if emit: + self.index.emit(index) + self.current_page_index = index + self.page_count = page_count + + def _goto_page(self, index: int): + self.update_buttons(self.page_count, index) + + def _assign_click(self, button: QPushButton, index: int): + with catch_warnings(record=True): + button.clicked.disconnect() + button.clicked.connect(lambda checked=False, i=index: self._goto_page(i)) + + @staticmethod + def _get_button_at(layout: QHBoxLayout, index: int) -> QPushButton | None: + """Safely return a button in a layout at a given index, if it exists.""" + item = layout.itemAt(index) + widget = item.widget() if item else None + return widget if isinstance(widget, QPushButton) else None + + @override + def eventFilter(self, watched: QObject, event: QEvent) -> bool: + if watched is self.parentWidget() and event.type() == QEvent.Type.Resize: + self._sync_geometry() + return super().eventFilter(watched, event) + + def _sync_geometry(self): + parent = self.parentWidget() + if parent is None: + return + # Stays out of the way of the entry view scrollbar + right_inset = max(self._RIGHT_MARGIN, self._SCROLLBAR_WIDTH) + width = max(0, parent.width() - right_inset) + self.setGeometry(0, parent.height() - self.HEIGHT, width, self.HEIGHT) diff --git a/src/tagstudio/qt/mixed/pagination.py b/src/tagstudio/qt/mixed/pagination.py deleted file mode 100644 index d1a26bcd..00000000 --- a/src/tagstudio/qt/mixed/pagination.py +++ /dev/null @@ -1,329 +0,0 @@ -# SPDX-FileCopyrightText: (c) TagStudio Contributors -# SPDX-License-Identifier: GPL-3.0-only - -# pyright: reportOptionalMemberAccess=false - -from typing import cast, override -from warnings import catch_warnings - -from PIL import Image, ImageQt -from PySide6.QtCore import QEvent, QObject, QSize, Qt, Signal -from PySide6.QtGui import QIntValidator, QPixmap -from PySide6.QtWidgets import QHBoxLayout, QLabel, QLineEdit, QPushButton, QWidget - -from tagstudio.qt.resource_manager import ResourceManager -from tagstudio.qt.views.styles.color_overlay import auto_theme_overlay -from tagstudio.qt.views.styles.stylesheets import pagination_style - - -# TODO: Split to use MVC guidelines. -class Pagination(QWidget): - """Widget containing controls for navigating between pages of items.""" - - HEIGHT = 36 # Button row height (24) plus 6px above and below. - RIGHT_MARGIN = 6 - SCROLLBAR_WIDTH = 14 - - index = Signal(int) - - def __init__(self, parent: QWidget | None = None) -> None: - super().__init__(parent=parent) - self.rm = ResourceManager() - self.page_count: int = 0 - self.current_page_index: int = 0 - self.buffer_page_count: int = 4 - self.button_size = QSize(32, 24) - - # ------------ UI EXAMPLE -------------- - # [<] [1]...[3][4] [5] [6][7]...[42] [>] - # ^^^^ <-- 2 Buffer Pages - # Center Page Number is Editable Text - # -------------------------------------- - - # [----------- ROOT LAYOUT ------------] - self.setHidden(True) - self.setObjectName("pagination") - self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground) - self.setFixedHeight(self.HEIGHT) - self.setStyleSheet(pagination_style()) - self.root_layout = QHBoxLayout(self) - self.root_layout.setContentsMargins(0, 6, 0, 6) - self.root_layout.setSpacing(3) - - if parent is not None: - parent.installEventFilter(self) - self._sync_geometry() - - # [<] ---------------------------------- - self.prev_button = QPushButton() - prev_icon: Image.Image = self.rm.bxs_left_arrow - prev_icon = auto_theme_overlay(prev_icon, use_alpha=False) - self.prev_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(prev_icon))) - self.prev_button.setIconSize(QSize(12, 12)) - self.prev_button.setMinimumSize(self.button_size) - self.prev_button.setMaximumSize(self.button_size) - - # --- [1] ------------------------------ - self.start_button = QPushButton() - self.start_button.setMinimumSize(self.button_size) - self.start_button.setMaximumSize(self.button_size) - - # ------ ... --------------------------- - self.start_ellipses = QLabel() - self.start_ellipses.setMinimumSize(self.button_size) - self.start_ellipses.setMaximumSize(self.button_size) - # self.start_ellipses.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.start_ellipses.setText(". . .") - - # --------- [3][4] --------------------- - self.start_buffer_container = QWidget() - self.start_buffer_layout = QHBoxLayout(self.start_buffer_container) - self.start_buffer_layout.setContentsMargins(0, 0, 0, 0) - self.start_buffer_layout.setSpacing(3) - - # ---------------- [5] ----------------- - self.current_page_field = QLineEdit() - self.current_page_field.setMinimumSize(self.button_size) - self.current_page_field.setMaximumSize(self.button_size) - self.validator = Validator(1, self.page_count) - self.current_page_field.setValidator(self.validator) - self.current_page_field.returnPressed.connect( - lambda: self._goto_page(int(self.current_page_field.text()) - 1) - ) - - # -------------------- [6][7] ---------- - self.end_buffer_container = QWidget() - self.end_buffer_layout = QHBoxLayout(self.end_buffer_container) - self.end_buffer_layout.setContentsMargins(0, 0, 0, 0) - self.end_buffer_layout.setSpacing(3) - - # -------------------------- ... ------- - self.end_ellipses = QLabel() - self.end_ellipses.setMinimumSize(self.button_size) - self.end_ellipses.setMaximumSize(self.button_size) - # self.end_ellipses.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.end_ellipses.setText(". . .") - - # ----------------------------- [42] --- - self.end_button = QPushButton() - self.end_button.setMinimumSize(self.button_size) - self.end_button.setMaximumSize(self.button_size) - - # ---------------------------------- [>] - self.next_button = QPushButton() - next_icon: Image.Image = self.rm.bxs_right_arrow - next_icon = auto_theme_overlay(next_icon, use_alpha=False) - self.next_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(next_icon))) - self.next_button.setIconSize(QSize(12, 12)) - self.next_button.setMinimumSize(self.button_size) - self.next_button.setMaximumSize(self.button_size) - - # Add Widgets to Root Layout - self.root_layout.addStretch(1) - self.root_layout.addWidget(self.prev_button) - self.root_layout.addWidget(self.start_button) - self.root_layout.addWidget(self.start_ellipses) - self.root_layout.addWidget(self.start_buffer_container) - self.root_layout.addWidget(self.current_page_field) - self.root_layout.addWidget(self.end_buffer_container) - self.root_layout.addWidget(self.end_ellipses) - self.root_layout.addWidget(self.end_button) - self.root_layout.addWidget(self.next_button) - self.root_layout.addStretch(1) - - self._populate_buffer_buttons() - - def update_buttons(self, page_count: int, index: int, emit: bool = True): - # Guard - if index < 0: - raise ValueError("Negative index detected") - - for i in range(0, 10): - if self.start_buffer_layout.itemAt(i): - self.start_buffer_layout.itemAt(i).widget().setHidden(True) - if self.end_buffer_layout.itemAt(i): - self.end_buffer_layout.itemAt(i).widget().setHidden(True) - - end_page = page_count - 1 - if page_count <= 1: - # Hide everything if there are only one or less pages. - # [-------------- HIDDEN --------------] - self.setHidden(True) - - elif page_count > 1: - # Enable/Disable Next+Prev Buttons - if index == 0: - self.prev_button.setDisabled(True) - else: - self._assign_click(self.prev_button, index - 1) - self.prev_button.setDisabled(False) - - if index == end_page: - self.next_button.setDisabled(True) - else: - self._assign_click(self.next_button, index + 1) - self.next_button.setDisabled(False) - - # Set Ellipses Sizes - if 8 <= page_count <= 11: - end_scale = max(1, page_count - index - 6) - srt_scale = max(1, index - 5) - elif page_count > 11: - end_scale = max(1, 7 - index) - srt_scale = max(1, (7 - (end_page - index))) - - if page_count >= 8: - end_size = self.button_size.width() * end_scale + (3 * (end_scale - 1)) # pyright: ignore[reportPossiblyUnboundVariable] - srt_size = self.button_size.width() * srt_scale + (3 * (srt_scale - 1)) # pyright: ignore[reportPossiblyUnboundVariable] - self.end_ellipses.setMinimumWidth(end_size) - self.end_ellipses.setMaximumWidth(end_size) - self.start_ellipses.setMinimumWidth(srt_size) - self.start_ellipses.setMaximumWidth(srt_size) - - # Enable/Disable Ellipses - if index <= self.buffer_page_count + 1: - self.start_ellipses.setHidden(True) - else: - self.start_ellipses.setHidden(False) - self._assign_click(self.start_button, 0) - if index >= (page_count - self.buffer_page_count - 2): - self.end_ellipses.setHidden(True) - else: - self.end_ellipses.setHidden(False) - - # Hide/Unhide Start+End Buttons - if index != 0: - self.start_button.setText("1") - self._assign_click(self.start_button, 0) - self.start_button.setHidden(False) - else: - self.start_button.setHidden(True) - if index != page_count - 1: - self.end_button.setText(str(page_count)) - self._assign_click(self.end_button, page_count - 1) - self.end_button.setHidden(False) - else: - self.end_button.setHidden(True) - - if index == 0 or index == 1: - self.start_buffer_container.setHidden(True) - else: - self.start_buffer_container.setHidden(False) - - if index == page_count - 1 or index == page_count - 2: - self.end_buffer_container.setHidden(True) - else: - self.end_buffer_container.setHidden(False) - - # Current Field and Buffer Pages - sbc = 0 - for i in range(0, page_count): - # Set Field - if i == index: - if self.start_buffer_layout.itemAt(i): - self.start_buffer_layout.itemAt(i).widget().setHidden(True) - if self.end_buffer_layout.itemAt(i): - self.end_buffer_layout.itemAt(i).widget().setHidden(True) - sbc += 1 - self.current_page_field.setText(str(i + 1)) - - start_offset = max(0, (index - 4) - 4) - end_offset = min(page_count - 1, (index + 4) - 4) - if i < index: - if (i != 0) and i >= index - 4: - self.start_buffer_layout.itemAt(i - start_offset).widget().setHidden(False) - self.start_buffer_layout.itemAt(i - start_offset).widget().setText( # pyright: ignore[reportAttributeAccessIssue] - str(i + 1) - ) - self._assign_click( - cast( - QPushButton, - self.start_buffer_layout.itemAt(i - start_offset).widget(), - ), - i, - ) - sbc += 1 - else: - if self.start_buffer_layout.itemAt(i): - self.start_buffer_layout.itemAt(i).widget().setHidden(True) - if self.end_buffer_layout.itemAt(i): - self.end_buffer_layout.itemAt(i).widget().setHidden(True) - elif i > index: - if i != page_count - 1 and i <= index + 4: - self.end_buffer_layout.itemAt(i - end_offset).widget().setHidden(False) - self.end_buffer_layout.itemAt(i - end_offset).widget().setText(str(i + 1)) # pyright: ignore[reportAttributeAccessIssue] - self._assign_click( - cast( - QPushButton, - self.end_buffer_layout.itemAt(i - end_offset).widget(), - ), - i, - ) - else: - if self.end_buffer_layout.itemAt(i): - self.end_buffer_layout.itemAt(i).widget().setHidden(True) - for j in range(0, self.buffer_page_count): - if self.end_buffer_layout.itemAt(i - end_offset + j): - self.end_buffer_layout.itemAt( - i - end_offset + j - ).widget().setHidden(True) - - if self.start_buffer_layout.itemAt(i - 1): - self.start_buffer_layout.itemAt(i - 1).widget().setHidden(True) - - self.setHidden(False) - self._sync_geometry() - self.raise_() - - self.validator.setTop(page_count) - if emit: - self.index.emit(index) - self.current_page_index = index - self.page_count = page_count - - def _goto_page(self, index: int): - self.update_buttons(self.page_count, index) - - def _assign_click(self, button: QPushButton, index: int): - with catch_warnings(record=True): - button.clicked.disconnect() - button.clicked.connect(lambda checked=False, i=index: self._goto_page(i)) - - def _populate_buffer_buttons(self): - for _ in range(max(self.buffer_page_count * 2, 5)): - button = QPushButton() - button.setMinimumSize(self.button_size) - button.setMaximumSize(self.button_size) - button.setHidden(True) - self.start_buffer_layout.addWidget(button) - - end_button = QPushButton() - end_button.setMinimumSize(self.button_size) - end_button.setMaximumSize(self.button_size) - end_button.setHidden(True) - self.end_buffer_layout.addWidget(end_button) - - @override - def eventFilter(self, watched: QObject, event: QEvent) -> bool: - if watched is self.parentWidget() and event.type() == QEvent.Type.Resize: - self._sync_geometry() - return super().eventFilter(watched, event) - - def _sync_geometry(self): - parent = self.parentWidget() - if parent is None: - return - # Stays out of the way of the entry view scrollbar - right_inset = max(self.RIGHT_MARGIN, self.SCROLLBAR_WIDTH) - width = max(0, parent.width() - right_inset) - self.setGeometry(0, parent.height() - self.HEIGHT, width, self.HEIGHT) - - -class Validator(QIntValidator): - def __init__(self, bottom: int, top: int) -> None: - super().__init__(bottom, top) - - @override - def fixup(self, input: str) -> str: - input = input.strip("0") - return super().fixup(str(self.top()) if input else "1") diff --git a/src/tagstudio/qt/views/pagination_view.py b/src/tagstudio/qt/views/pagination_view.py new file mode 100644 index 00000000..d9458381 --- /dev/null +++ b/src/tagstudio/qt/views/pagination_view.py @@ -0,0 +1,120 @@ +# SPDX-FileCopyrightText: (c) TagStudio Contributors +# SPDX-License-Identifier: GPL-3.0-only + + +from typing import override + +from PIL import Image, ImageQt +from PySide6.QtCore import QSize +from PySide6.QtGui import QIntValidator, QPixmap, Qt +from PySide6.QtWidgets import QHBoxLayout, QLabel, QLineEdit, QPushButton, QWidget + +from tagstudio.qt.resource_manager import ResourceManager +from tagstudio.qt.views.styles.color_overlay import auto_theme_overlay + + +class PageValidator(QIntValidator): + @override + def fixup(self, input: str) -> str: + input = input.strip("0") + return super().fixup(str(self.top()) if input else "1") + + +class PaginationView(QHBoxLayout): + # NOTE: UI Example: + # [<] [1]...[3][4] [5] [6][7]...[42] [>] + # ^^^^ <-- 2 Buffer Pages + # Center Page Number is Editable Text + + BUTTON_SIZE = QSize(32, 24) + BUFFER_PAGE_COUNT = 4 + + def __init__(self) -> None: + super().__init__() + self.setContentsMargins(0, 6, 0, 6) + self.setSpacing(3) + _rm = ResourceManager() + + # [<] ---------------------------------- + self.prev_button = QPushButton() + prev_icon: Image.Image = auto_theme_overlay(_rm.bxs_left_arrow, use_alpha=False) + self.prev_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(prev_icon))) + self.prev_button.setIconSize(QSize(12, 12)) + self.prev_button.setMinimumSize(self.BUTTON_SIZE) + self.prev_button.setMaximumSize(self.BUTTON_SIZE) + + # --- [1] ------------------------------ + self.start_button = QPushButton() + self.start_button.setMinimumSize(self.BUTTON_SIZE) + self.start_button.setMaximumSize(self.BUTTON_SIZE) + + # ------ ... --------------------------- + self.start_ellipses = QLabel(". . .") + self.start_ellipses.setMinimumSize(self.BUTTON_SIZE) + self.start_ellipses.setMaximumSize(self.BUTTON_SIZE) + + # --------- [3][4] --------------------- + self.start_buffer_container = QWidget() + self.start_buffer_layout = QHBoxLayout(self.start_buffer_container) + self.start_buffer_layout.setContentsMargins(0, 0, 0, 0) + self.start_buffer_layout.setSpacing(3) + + # ---------------- [5] ----------------- + self.current_page_field = QLineEdit() + self.current_page_field.setMinimumSize(self.BUTTON_SIZE) + self.current_page_field.setMaximumSize(self.BUTTON_SIZE) + self.current_page_field.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.validator = PageValidator(1, 0) + self.current_page_field.setValidator(self.validator) + + # -------------------- [6][7] ---------- + self.end_buffer_container = QWidget() + self.end_buffer_layout = QHBoxLayout(self.end_buffer_container) + self.end_buffer_layout.setContentsMargins(0, 0, 0, 0) + self.end_buffer_layout.setSpacing(3) + + # -------------------------- ... ------- + self.end_ellipses = QLabel(". . .") + self.end_ellipses.setMinimumSize(self.BUTTON_SIZE) + self.end_ellipses.setMaximumSize(self.BUTTON_SIZE) + + # ----------------------------- [42] --- + self.end_button = QPushButton() + self.end_button.setMinimumSize(self.BUTTON_SIZE) + self.end_button.setMaximumSize(self.BUTTON_SIZE) + + # ---------------------------------- [>] + self.next_button = QPushButton() + next_icon: Image.Image = auto_theme_overlay(_rm.bxs_right_arrow, use_alpha=False) + self.next_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(next_icon))) + self.next_button.setIconSize(QSize(12, 12)) + self.next_button.setMinimumSize(self.BUTTON_SIZE) + self.next_button.setMaximumSize(self.BUTTON_SIZE) + + # Finalize Layout + self.addStretch(1) + self.addWidget(self.prev_button) + self.addWidget(self.start_button) + self.addWidget(self.start_ellipses) + self.addWidget(self.start_buffer_container) + self.addWidget(self.current_page_field) + self.addWidget(self.end_buffer_container) + self.addWidget(self.end_ellipses) + self.addWidget(self.end_button) + self.addWidget(self.next_button) + self.addStretch(1) + self._populate_buffer_buttons() + + def _populate_buffer_buttons(self) -> None: + for _ in range(max(self.BUFFER_PAGE_COUNT * 2, 5)): + button = QPushButton() + button.setMinimumSize(self.BUTTON_SIZE) + button.setMaximumSize(self.BUTTON_SIZE) + button.setHidden(True) + self.start_buffer_layout.addWidget(button) + + end_button = QPushButton() + end_button.setMinimumSize(self.BUTTON_SIZE) + end_button.setMaximumSize(self.BUTTON_SIZE) + end_button.setHidden(True) + self.end_buffer_layout.addWidget(end_button)