Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions src/ui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ def __init__(self, config_manager=None):
self.input_manager = None

# Настройка прозрачного и безрамочного окна
self.setWindowFlags(
Qt.WindowStaysOnTopHint |
Qt.FramelessWindowHint |
Qt.Tool
)
flags = Qt.FramelessWindowHint | Qt.Tool
if self.config is None or self.config.get("always_on_top") is True:
flags |= Qt.WindowStaysOnTopHint
self.setWindowFlags(flags)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setWindowOpacity(self.config.get("opacity") / 100.0 if self.config else 1.0)

Expand Down Expand Up @@ -88,6 +87,22 @@ def moveEvent(self, event):
def get_cached_pos(self):
return self._cached_pos

def set_always_on_top(self, enabled):
"""Динамически включает или выключает режим 'Поверх всех окон'"""
if self.config:
self.config.set("always_on_top", enabled)

# Получаем текущие флаги окна
flags = self.windowFlags()
if enabled:
flags |= Qt.WindowStaysOnTopHint
else:
flags &= ~Qt.WindowStaysOnTopHint

self.setWindowFlags(flags)
# В Qt изменение флагов скрывает окно, поэтому его нужно перепоказать
self.show()

def set_opacity(self, value):
"""Устанавливает прозрачность окна (0-100)"""
self.setWindowOpacity(value / 100.0)
Expand Down
8 changes: 7 additions & 1 deletion src/ui/settings_dialog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PySide6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QSlider, QComboBox, QPushButton, QSpinBox
from PySide6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QSlider, QComboBox, QPushButton, QSpinBox, QCheckBox
from PySide6.QtCore import Qt
from src.utils.bonding_utils import CAT_SKINS

Expand Down Expand Up @@ -50,6 +50,11 @@ def __init__(self, config, parent=None):
self.pomodoro_break_spin.setValue(self.config.get("pomodoro_break"))
layout.addWidget(self.pomodoro_break_spin)

# Поверх всех окон
self.always_on_top_check = QCheckBox("Поверх всех окон")
self.always_on_top_check.setChecked(self.config.get("always_on_top"))
layout.addWidget(self.always_on_top_check)

# Выбор скина
layout.addWidget(QLabel("Окрас котика:"))
self.skin_combo = QComboBox()
Expand Down Expand Up @@ -79,5 +84,6 @@ def save_settings(self):
self.config.set("stretch_interval", self.stretch_spin.value())
self.config.set("pomodoro_work", self.pomodoro_work_spin.value())
self.config.set("pomodoro_break", self.pomodoro_break_spin.value())
self.config.set("always_on_top", self.always_on_top_check.isChecked())
self.config.set("skin", self.skin_combo.currentData())
self.accept()
13 changes: 13 additions & 0 deletions src/ui/tray_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ def setup_menu(self):
peek_action.triggered.connect(lambda checked=False: self.window.toggle_peek_mode())
self.menu.addAction(peek_action)

# Поверх всех окон
self.always_on_top_action = QAction("Поверх всех окон", self)
self.always_on_top_action.setCheckable(True)
self.always_on_top_action.setChecked(self.window.config.get("always_on_top") if self.window.config else True)
self.always_on_top_action.triggered.connect(self.toggle_always_on_top)
self.menu.addAction(self.always_on_top_action)

self.menu.addSeparator()

# Статистика
Expand Down Expand Up @@ -171,12 +178,18 @@ def start_break_timer(self, checked=False):
if self.window.input_manager and self.window.input_manager.db:
self.window.input_manager.db.log_event("pomodoro_start", "Начата сессия отдыха")

def toggle_always_on_top(self, checked):
self.window.set_always_on_top(checked)

def show_settings(self, checked=False):
dialog = SettingsDialog(self.window.config, self.window)
if dialog.exec():
# Обновляем скин и прозрачность в реальном времени
self.window.animation_manager.set_skin(self.window.config.get("skin"))
self.window.set_opacity(self.window.config.get("opacity"))
# Обновляем состояние "Поверх всех окон" в трее
self.always_on_top_action.setChecked(self.window.config.get("always_on_top"))
self.window.set_always_on_top(self.window.config.get("always_on_top"))
# Обновляем тексты в меню Pomodoro
self.update_pomodoro_menu_texts()
# Перезапускаем таймер растяжки с новым интервалом
Expand Down
24 changes: 24 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ def test_timer_system_pomodoro(self):
self.assertEqual(ts.pomodoro_state, "idle")
self.assertEqual(ts.pomodoro_remaining, 0)

def test_always_on_top_logic(self):
config = ConfigManager(self.config_path)
config.set("always_on_top", True)

# Test default and toggled state in config
self.assertTrue(config.get("always_on_top"))
config.set("always_on_top", False)
self.assertFalse(config.get("always_on_top"))

# Test PetWindow dynamic set_always_on_top (using mock since we are running headless without full UI initialization)
mock_window = MagicMock()
mock_window.config = config

def set_always_on_top_impl(enabled):
config.set("always_on_top", enabled)

mock_window.set_always_on_top = set_always_on_top_impl

mock_window.set_always_on_top(True)
self.assertTrue(config.get("always_on_top"))

mock_window.set_always_on_top(False)
self.assertFalse(config.get("always_on_top"))

def test_input_manager_petting_logic(self):
# Мокаем PetWindow, AnimationManager и SoundManager
mock_window = MagicMock()
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified verification/screenshots/stats_history.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.