|
10 | 10 | import logging |
11 | 11 | from typing import Callable |
12 | 12 |
|
13 | | -from PySide6.QtCore import Qt, Signal |
| 13 | +from PySide6.QtCore import QCoreApplication, QEvent, Qt, Signal |
14 | 14 | from PySide6.QtGui import QKeyEvent, QMouseEvent |
15 | 15 | from PySide6.QtWidgets import ( |
16 | 16 | QCheckBox, |
@@ -82,6 +82,9 @@ class SettingsDialog(QDialog): |
82 | 82 | ``None`` disables the calibrate button. |
83 | 83 | """ |
84 | 84 |
|
| 85 | + hotkey_capture_active_changed = Signal(bool) |
| 86 | + applied = Signal() |
| 87 | + |
85 | 88 | def __init__( |
86 | 89 | self, |
87 | 90 | config: AppConfig, |
@@ -168,7 +171,7 @@ def _build_general_tab(self) -> None: |
168 | 171 | self._hotkey_capture = HotkeyCaptureEdit() |
169 | 172 | self._hotkey_capture.captured.connect(self._on_hotkey_captured) |
170 | 173 | self._hotkey_capture.cancelled.connect(self._stop_hotkey_recording) |
171 | | - self._hotkey_record_btn = QPushButton("Record") |
| 174 | + self._hotkey_record_btn = QPushButton("Register Hotkey") |
172 | 175 | self._hotkey_record_btn.setCheckable(True) |
173 | 176 | self._hotkey_record_btn.clicked.connect(self._on_hotkey_record_clicked) |
174 | 177 | capture_row = QHBoxLayout() |
@@ -224,17 +227,23 @@ def _on_hotkey_preset_chosen(self, index: int) -> None: |
224 | 227 | self._set_captured_hotkey(hotkey) |
225 | 228 |
|
226 | 229 | def _start_hotkey_recording(self) -> None: |
| 230 | + if self._hotkey_capture.is_recording(): |
| 231 | + return |
227 | 232 | self._hotkey_record_btn.setChecked(True) |
228 | 233 | self._hotkey_record_btn.setText("Cancel") |
229 | 234 | self._hotkey_error.setVisible(False) |
| 235 | + self.hotkey_capture_active_changed.emit(True) |
230 | 236 | self._hotkey_capture.start_recording() |
231 | 237 |
|
232 | 238 | def _stop_hotkey_recording(self) -> None: |
| 239 | + if not self._hotkey_capture.is_recording(): |
| 240 | + return |
233 | 241 | self._hotkey_record_btn.setChecked(False) |
234 | | - self._hotkey_record_btn.setText("Record") |
| 242 | + self._hotkey_record_btn.setText("Register Hotkey") |
235 | 243 | self._hotkey_capture.stop_recording() |
236 | 244 | if self._captured_hotkey is not None: |
237 | 245 | self._hotkey_capture.show_hotkey(self._captured_hotkey) |
| 246 | + self.hotkey_capture_active_changed.emit(False) |
238 | 247 |
|
239 | 248 | def _on_hotkey_record_clicked(self, checked: bool) -> None: |
240 | 249 | if checked: |
@@ -554,22 +563,36 @@ def _on_reset(self) -> None: |
554 | 563 | def _on_apply(self) -> None: |
555 | 564 | """Apply: collect and persist without closing.""" |
556 | 565 | with log_duration(log, "Settings apply"): |
| 566 | + self._stop_hotkey_recording() |
557 | 567 | self._collect() |
558 | 568 | if not self._show_validation_issue(): |
559 | 569 | return |
560 | 570 | if not self._sync_startup_or_warn(): |
561 | 571 | return |
562 | 572 | save_config(self._working) |
| 573 | + self.applied.emit() |
563 | 574 | log.info("Settings applied") |
564 | 575 |
|
565 | 576 | # ------------------------------------------------------------------ |
566 | 577 | # Overrides |
567 | 578 | # ------------------------------------------------------------------ |
568 | 579 |
|
569 | 580 | def accept(self) -> None: |
| 581 | + self._stop_hotkey_recording() |
570 | 582 | self._collect() |
571 | 583 | super().accept() |
572 | 584 |
|
| 585 | + def reject(self) -> None: |
| 586 | + self._stop_hotkey_recording() |
| 587 | + super().reject() |
| 588 | + |
| 589 | + def done(self, result: int) -> None: |
| 590 | + self._stop_hotkey_recording() |
| 591 | + super().done(result) |
| 592 | + |
| 593 | + def is_hotkey_capture_active(self) -> bool: |
| 594 | + return self._hotkey_capture.is_recording() |
| 595 | + |
573 | 596 | def _show_validation_issue(self) -> bool: |
574 | 597 | issue = next(iter(validate_config(self._working)), None) |
575 | 598 | if issue is None: |
@@ -673,15 +696,23 @@ def is_recording(self) -> bool: |
673 | 696 | return self._recording |
674 | 697 |
|
675 | 698 | def start_recording(self) -> None: |
| 699 | + if self._recording: |
| 700 | + return |
676 | 701 | self._recording = True |
677 | | - self.setText("press keys or a mouse button…") |
| 702 | + self.setText("press the new hotkey…") |
678 | 703 | self.setFocus(Qt.OtherFocusReason) |
| 704 | + app = QCoreApplication.instance() |
| 705 | + if app is not None: |
| 706 | + app.installEventFilter(self) |
679 | 707 | self.grabKeyboard() |
680 | | - self.grabMouse() |
681 | 708 |
|
682 | 709 | def stop_recording(self) -> None: |
| 710 | + if not self._recording: |
| 711 | + return |
683 | 712 | self._recording = False |
684 | | - self.releaseMouse() |
| 713 | + app = QCoreApplication.instance() |
| 714 | + if app is not None: |
| 715 | + app.removeEventFilter(self) |
685 | 716 | self.releaseKeyboard() |
686 | 717 |
|
687 | 718 | def show_hotkey(self, hotkey: Hotkey) -> None: |
@@ -713,6 +744,16 @@ def mousePressEvent(self, event: QMouseEvent) -> None: |
713 | 744 | event.accept() |
714 | 745 | self.captured.emit(Hotkey(_mods_from_qt(event.modifiers()), "mouse", code)) |
715 | 746 |
|
| 747 | + def eventFilter(self, watched, event) -> bool: |
| 748 | + if not self._recording or event.type() != QEvent.Type.MouseButtonPress: |
| 749 | + return False |
| 750 | + code = _mouse_button_to_code(event.button()) |
| 751 | + if code is None: |
| 752 | + return False |
| 753 | + event.accept() |
| 754 | + self.captured.emit(Hotkey(_mods_from_qt(event.modifiers()), "mouse", code)) |
| 755 | + return True |
| 756 | + |
716 | 757 |
|
717 | 758 | def _combo_index(combo: QComboBox, data: str) -> int: |
718 | 759 | for i in range(combo.count()): |
|
0 commit comments