265 lines
10 KiB
Python
265 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import unittest
|
|
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
|
|
from PyQt6.QtCore import QEvent, Qt
|
|
from PyQt6.QtGui import QKeyEvent
|
|
from PyQt6.QtWidgets import QApplication, QLabel, QWidget
|
|
from qfluentwidgets import FluentIcon, LineEdit, PrimaryToolButton, PushButton, SettingCardGroup
|
|
|
|
from ui.fluent_widgets import (
|
|
QuickActionsBar,
|
|
SettingsCard,
|
|
_SettingCardGroupAutoHeightFilter,
|
|
enable_setting_card_group_auto_height,
|
|
insert_widget_into_setting_card_group,
|
|
)
|
|
from ui.presets_menu.toolbar import PresetsToolbarLayout
|
|
from ui.widgets.win11_controls import Win11ToggleRow
|
|
|
|
|
|
class QuickActionsBarLayoutTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
cls._app = QApplication.instance() or QApplication([])
|
|
|
|
def test_setting_card_group_height_keeps_quick_action_buttons_visible(self) -> None:
|
|
group = SettingCardGroup("Действия")
|
|
actions = QuickActionsBar()
|
|
actions.add_buttons(
|
|
[
|
|
PushButton("Открыть файл", icon=FluentIcon.LINK),
|
|
PushButton("Сбросить файл", icon=FluentIcon.RETURN),
|
|
PushButton("Очистить всё", icon=FluentIcon.DELETE),
|
|
]
|
|
)
|
|
|
|
insert_widget_into_setting_card_group(group, 1, actions)
|
|
|
|
needed_height = group.vBoxLayout.minimumSize().height()
|
|
self.assertGreaterEqual(group.minimumHeight(), needed_height)
|
|
self.assertGreaterEqual(group.maximumHeight(), needed_height)
|
|
self.assertGreaterEqual(actions.sizeHint().height(), actions.actions_layout.minimumSize().height())
|
|
|
|
def test_presets_toolbar_places_search_on_the_right_when_width_allows(self) -> None:
|
|
parent = QWidget()
|
|
toolbar = PresetsToolbarLayout(parent, row_count=3, button_spacing=8)
|
|
first = PushButton("Импорт")
|
|
second = PushButton("Папка")
|
|
first.setFixedWidth(80)
|
|
second.setFixedWidth(80)
|
|
search = LineEdit()
|
|
|
|
toolbar.set_buttons([first, second])
|
|
toolbar.set_trailing_widget(search, minimum_width=180)
|
|
toolbar.refresh_layout(420)
|
|
|
|
row_layout = toolbar._rows[0][1]
|
|
widgets = [row_layout.itemAt(index).widget() for index in range(row_layout.count())]
|
|
|
|
self.assertEqual(widgets[:2], [first, second])
|
|
self.assertIn(search, widgets)
|
|
|
|
def test_presets_toolbar_action_button_is_named_and_keyboard_clickable(self) -> None:
|
|
parent = QWidget()
|
|
toolbar = PresetsToolbarLayout(parent)
|
|
button = toolbar.create_action_button("Импорт", FluentIcon.DOWNLOAD)
|
|
clicked: list[bool] = []
|
|
button.clicked.connect(lambda: clicked.append(True))
|
|
|
|
event = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_Return, Qt.KeyboardModifier.NoModifier)
|
|
button.keyPressEvent(event)
|
|
|
|
self.assertEqual(button.accessibleName(), "Импорт")
|
|
self.assertEqual(button.property("screenReaderStateText"), "Импорт")
|
|
self.assertEqual(button.focusPolicy(), Qt.FocusPolicy.StrongFocus)
|
|
self.assertTrue(event.isAccepted())
|
|
self.assertEqual(clicked, [True])
|
|
|
|
def test_presets_toolbar_icon_button_can_be_named_for_screen_reader(self) -> None:
|
|
parent = QWidget()
|
|
toolbar = PresetsToolbarLayout(parent)
|
|
button = toolbar.create_primary_tool_button(
|
|
PrimaryToolButton,
|
|
FluentIcon.ADD,
|
|
accessible_name="Создать пресет",
|
|
accessible_description="Создаёт новый пользовательский пресет.",
|
|
)
|
|
|
|
self.assertEqual(button.accessibleName(), "Создать пресет")
|
|
self.assertEqual(button.property("screenReaderStateText"), "Создать пресет")
|
|
self.assertIn("новый пользовательский пресет", button.accessibleDescription())
|
|
self.assertEqual(button.focusPolicy(), Qt.FocusPolicy.StrongFocus)
|
|
|
|
def test_presets_toolbar_moves_search_to_next_row_when_width_is_tight(self) -> None:
|
|
parent = QWidget()
|
|
toolbar = PresetsToolbarLayout(parent, row_count=3, button_spacing=8)
|
|
first = PushButton("Импорт")
|
|
second = PushButton("Папка")
|
|
first.setFixedWidth(80)
|
|
second.setFixedWidth(80)
|
|
search = LineEdit()
|
|
|
|
toolbar.set_buttons([first, second])
|
|
toolbar.set_trailing_widget(search, minimum_width=180)
|
|
toolbar.refresh_layout(230)
|
|
|
|
first_row_widgets = [
|
|
toolbar._rows[0][1].itemAt(index).widget()
|
|
for index in range(toolbar._rows[0][1].count())
|
|
]
|
|
second_row_widgets = [
|
|
toolbar._rows[1][1].itemAt(index).widget()
|
|
for index in range(toolbar._rows[1][1].count())
|
|
]
|
|
|
|
self.assertIn(first, first_row_widgets)
|
|
self.assertIn(second, first_row_widgets)
|
|
self.assertIn(search, second_row_widgets)
|
|
|
|
def test_presets_toolbar_reserves_height_when_search_moves_to_next_row(self) -> None:
|
|
parent = QWidget()
|
|
toolbar = PresetsToolbarLayout(parent, row_count=3, button_spacing=8)
|
|
first = PushButton("Импорт")
|
|
second = PushButton("Папка")
|
|
first.setFixedWidth(80)
|
|
second.setFixedWidth(80)
|
|
search = LineEdit()
|
|
|
|
toolbar.set_buttons([first, second])
|
|
toolbar.set_trailing_widget(search, minimum_width=180)
|
|
toolbar.refresh_layout(230)
|
|
|
|
needed_height = toolbar.container.layout().sizeHint().height()
|
|
|
|
self.assertGreater(needed_height, toolbar._rows[0][0].sizeHint().height())
|
|
self.assertGreaterEqual(toolbar.container.minimumHeight(), needed_height)
|
|
self.assertGreaterEqual(toolbar.container.maximumHeight(), needed_height)
|
|
|
|
def test_presets_toolbar_uses_visible_container_width_for_wrapping(self) -> None:
|
|
parent = QWidget()
|
|
toolbar = PresetsToolbarLayout(parent, row_count=3, button_spacing=8)
|
|
first = PushButton("Импорт")
|
|
second = PushButton("Папка")
|
|
first.setFixedWidth(80)
|
|
second.setFixedWidth(80)
|
|
search = LineEdit()
|
|
|
|
toolbar.set_buttons([first, second])
|
|
toolbar.set_trailing_widget(search, minimum_width=180)
|
|
toolbar.container.setFixedWidth(230)
|
|
parent.show()
|
|
self._app.processEvents()
|
|
|
|
toolbar.refresh_layout(420)
|
|
|
|
first_row_widgets = [
|
|
toolbar._rows[0][1].itemAt(index).widget()
|
|
for index in range(toolbar._rows[0][1].count())
|
|
]
|
|
second_row_widgets = [
|
|
toolbar._rows[1][1].itemAt(index).widget()
|
|
for index in range(toolbar._rows[1][1].count())
|
|
]
|
|
|
|
self.assertIn(first, first_row_widgets)
|
|
self.assertIn(second, first_row_widgets)
|
|
self.assertNotIn(search, first_row_widgets)
|
|
self.assertIn(search, second_row_widgets)
|
|
|
|
def test_presets_toolbar_skips_relayout_when_state_is_unchanged(self) -> None:
|
|
parent = QWidget()
|
|
toolbar = PresetsToolbarLayout(parent, row_count=3, button_spacing=8)
|
|
first = PushButton("Импорт")
|
|
second = PushButton("Папка")
|
|
search = LineEdit()
|
|
|
|
toolbar.set_buttons([first, second])
|
|
toolbar.set_trailing_widget(search, minimum_width=180)
|
|
toolbar.refresh_layout(420)
|
|
|
|
first_row_layout = toolbar._rows[0][1]
|
|
original_clear_row = toolbar._clear_row
|
|
clear_calls: list[int] = []
|
|
|
|
def _count_clear(row_layout):
|
|
clear_calls.append(row_layout.count())
|
|
original_clear_row(row_layout)
|
|
|
|
toolbar._clear_row = _count_clear
|
|
toolbar.refresh_layout(420)
|
|
|
|
self.assertEqual(clear_calls, [])
|
|
self.assertIs(first_row_layout.itemAt(0).widget(), first)
|
|
self.assertIs(first_row_layout.itemAt(1).widget(), second)
|
|
|
|
def test_setting_card_group_height_updates_after_late_card_addition(self) -> None:
|
|
group = SettingCardGroup("Управление")
|
|
actions = QuickActionsBar()
|
|
actions.add_buttons(
|
|
[
|
|
PushButton("Открыть", icon=FluentIcon.FOLDER),
|
|
PushButton("Перестроить", icon=FluentIcon.SYNC),
|
|
]
|
|
)
|
|
|
|
insert_widget_into_setting_card_group(group, 1, actions)
|
|
|
|
info_card = SettingsCard()
|
|
info_card.add_widget(QLabel("Загрузка информации..."))
|
|
group.addSettingCard(info_card)
|
|
|
|
group.resize(638, 200)
|
|
group.show()
|
|
self._app.processEvents()
|
|
|
|
needed_info_bottom = info_card.geometry().y() + info_card.sizeHint().height()
|
|
self.assertGreaterEqual(group.minimumHeight(), needed_info_bottom)
|
|
self.assertGreaterEqual(group.maximumHeight(), needed_info_bottom)
|
|
self.assertLessEqual(group.minimumHeight(), needed_info_bottom + 1)
|
|
self.assertLessEqual(group.maximumHeight(), needed_info_bottom + 1)
|
|
|
|
def test_setting_card_group_auto_height_keeps_fluent_setting_card_height(self) -> None:
|
|
group = SettingCardGroup("Настройки программы")
|
|
rows = [
|
|
Win11ToggleRow(
|
|
"fa5s.bolt",
|
|
"Автозапуск DPI после старта программы",
|
|
"После запуска ZapretGUI автоматически запускать текущий DPI-режим",
|
|
),
|
|
Win11ToggleRow(
|
|
"fa5s.shield-alt",
|
|
"Отключить Windows Defender",
|
|
"Требуются права администратора",
|
|
),
|
|
]
|
|
original_heights = [row.minimumHeight() for row in rows]
|
|
|
|
for row in rows:
|
|
group.addSettingCard(row)
|
|
|
|
group.resize(710, 300)
|
|
group.show()
|
|
self._app.processEvents()
|
|
|
|
enable_setting_card_group_auto_height(group)
|
|
self._app.processEvents()
|
|
|
|
for row, original_height in zip(rows, original_heights):
|
|
self.assertGreaterEqual(row.minimumHeight(), original_height)
|
|
self.assertGreaterEqual(row.maximumHeight(), original_height)
|
|
|
|
def test_setting_card_group_auto_height_does_not_refresh_on_own_layout_requests(self) -> None:
|
|
events = _SettingCardGroupAutoHeightFilter._EVENTS
|
|
|
|
self.assertNotIn(QEvent.Type.LayoutRequest, events)
|
|
self.assertNotIn(QEvent.Type.Resize, events)
|
|
self.assertIn(QEvent.Type.ChildAdded, events)
|
|
self.assertIn(QEvent.Type.ChildRemoved, events)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|