|
22 | 22 | (see ``_visual_whitepoint_editor_measure_handler``), mirroring how wx's button |
23 | 23 | called back into the parent's ``ambient_measure_handler``. Still deliberately |
24 | 24 | dropped: the network **pattern-generator** patch output (tracked separately). |
25 | | -The custom wx spinners/sliders/AUI docking are replaced by native Qt widgets. |
| 25 | +The custom wx spinners/sliders are replaced by native Qt widgets; wx's |
| 26 | +AUI-managed pin/float pane has no docking-framework equivalent under Qt, so |
| 27 | +:meth:`VisualWhitepointEditorWindow.float_panel`/``dock_panel`` instead detach |
| 28 | +the controls panel into a plain top-level :class:`_FloatingControlsWindow` |
| 29 | +and re-embed it, toggled via the header row's pin button. |
26 | 30 | """ |
27 | 31 |
|
28 | 32 | from __future__ import annotations |
@@ -795,6 +799,35 @@ def restore_display_profiles( |
795 | 799 | thread.join() |
796 | 800 |
|
797 | 801 |
|
| 802 | +class _FloatingControlsWindow(QWidget): |
| 803 | + """Floating window that hosts the detached controls panel. |
| 804 | +
|
| 805 | + Qt port of wx's AUI-floated ``mainPanel`` pane (see wx's |
| 806 | + ``PinButton(True)`` / ``float_pane_handler``). There is no AUI-docking |
| 807 | + equivalent under Qt, so this is a plain top-level ``QWidget`` (no modal |
| 808 | + dialog semantics, e.g. no Escape-to-close) that clicking its native close |
| 809 | + button re-docks rather than destroys, mirroring wx's |
| 810 | + ``close_pane_handler`` (which vetoes the close and docks the pane |
| 811 | + instead). |
| 812 | +
|
| 813 | + Args: |
| 814 | + editor (VisualWhitepointEditorWindow): The owning editor. |
| 815 | + """ |
| 816 | + |
| 817 | + def __init__(self, editor: VisualWhitepointEditorWindow) -> None: |
| 818 | + super().__init__(editor, Qt.Tool) |
| 819 | + self._editor = editor |
| 820 | + |
| 821 | + def closeEvent(self, event: QCloseEvent) -> None: # noqa: N802 (Qt override) |
| 822 | + """Dock the panel back into the editor instead of closing. |
| 823 | +
|
| 824 | + Args: |
| 825 | + event (QCloseEvent): The Qt close event. |
| 826 | + """ |
| 827 | + event.ignore() |
| 828 | + self._editor.dock_panel() |
| 829 | + |
| 830 | + |
798 | 831 | class VisualWhitepointEditorWindow(BaseWindow): |
799 | 832 | """Standalone visual whitepoint editor window.""" |
800 | 833 |
|
@@ -824,6 +857,7 @@ def __init__(self) -> None: |
824 | 857 | self.display_size_mm: dict[tuple, list[float]] = {} |
825 | 858 | self.default_size = 300.0 |
826 | 859 | self._pm: _ProfileManager | None = None |
| 860 | + self._float_window: _FloatingControlsWindow | None = None |
827 | 861 |
|
828 | 862 | cfg_x, cfg_y, cfg_scale = ( |
829 | 863 | float(v) |
@@ -861,7 +895,16 @@ def _build_ui(self, cfg_x: float, cfg_y: float, cfg_scale: float) -> None: |
861 | 895 | panel.setContentsMargins(12, 12, 12, 12) |
862 | 896 | panel.setSpacing(10) |
863 | 897 |
|
864 | | - panel.addWidget(_section_label(lang.getstr("whitepoint"))) |
| 898 | + header_row = QHBoxLayout() |
| 899 | + header_row.addWidget(_section_label(lang.getstr("whitepoint"))) |
| 900 | + header_row.addStretch(1) |
| 901 | + self.pin_btn = _icon_button( |
| 902 | + "button-pin", lang.getstr("whitepoint.visual_editor.panel.float") |
| 903 | + ) |
| 904 | + self.pin_btn.setCheckable(True) |
| 905 | + self.pin_btn.toggled.connect(self._on_pin_toggled) |
| 906 | + header_row.addWidget(self.pin_btn) |
| 907 | + panel.addLayout(header_row) |
865 | 908 |
|
866 | 909 | self.hsv_wheel = HSVWheel(self) |
867 | 910 | self.bright_ctrl = BrightCtrl(self, self.colour) |
@@ -947,6 +990,8 @@ def _build_ui(self, cfg_x: float, cfg_y: float, cfg_scale: float) -> None: |
947 | 990 | panel.addStretch(1) |
948 | 991 |
|
949 | 992 | self.bg_area = _BackgroundArea(central) |
| 993 | + self.controls = controls |
| 994 | + self._root_layout = root |
950 | 995 | root.addWidget(controls, 0) |
951 | 996 | root.addWidget(self.bg_area, 1) |
952 | 997 | self.setCentralWidget(central) |
@@ -1093,6 +1138,51 @@ def _center_slider(slider: QSlider) -> None: |
1093 | 1138 | """ |
1094 | 1139 | slider.setValue(500) |
1095 | 1140 |
|
| 1141 | + # -- controls panel float/dock ------------------------------------------ |
| 1142 | + |
| 1143 | + def _on_pin_toggled(self, checked: bool) -> None: |
| 1144 | + """Float or dock the controls panel in response to the pin button. |
| 1145 | +
|
| 1146 | + Args: |
| 1147 | + checked (bool): True to float the panel, False to dock it. |
| 1148 | + """ |
| 1149 | + if checked: |
| 1150 | + self.float_panel() |
| 1151 | + else: |
| 1152 | + self.dock_panel() |
| 1153 | + |
| 1154 | + def float_panel(self) -> None: |
| 1155 | + """Detach the controls panel into its own top-level window.""" |
| 1156 | + if self._float_window is not None: |
| 1157 | + return |
| 1158 | + self._root_layout.removeWidget(self.controls) |
| 1159 | + float_window = _FloatingControlsWindow(self) |
| 1160 | + layout = QVBoxLayout(float_window) |
| 1161 | + layout.setContentsMargins(0, 0, 0, 0) |
| 1162 | + layout.addWidget(self.controls) |
| 1163 | + self.controls.show() |
| 1164 | + self._float_window = float_window |
| 1165 | + top_left = self.mapToGlobal(QPoint(0, 0)) |
| 1166 | + float_window.move(top_left.x() + 10, top_left.y() + 10) |
| 1167 | + float_window.resize(self.controls.sizeHint()) |
| 1168 | + float_window.show() |
| 1169 | + self.pin_btn.setToolTip(lang.getstr("whitepoint.visual_editor.panel.dock")) |
| 1170 | + |
| 1171 | + def dock_panel(self) -> None: |
| 1172 | + """Re-embed a floating controls panel back into the editor window.""" |
| 1173 | + if self._float_window is None: |
| 1174 | + return |
| 1175 | + float_window = self._float_window |
| 1176 | + self._float_window = None |
| 1177 | + self.controls.setParent(None) |
| 1178 | + self._root_layout.insertWidget(0, self.controls, 0) |
| 1179 | + self.controls.show() |
| 1180 | + float_window.deleteLater() |
| 1181 | + self.pin_btn.blockSignals(True) |
| 1182 | + self.pin_btn.setChecked(False) |
| 1183 | + self.pin_btn.blockSignals(False) |
| 1184 | + self.pin_btn.setToolTip(lang.getstr("whitepoint.visual_editor.panel.float")) |
| 1185 | + |
1096 | 1186 | def apply_initial_whitepoint(self, r: int, g: int, b: int) -> None: |
1097 | 1187 | """Seed the editor from a display profile's ``vcgt`` whitepoint. |
1098 | 1188 |
|
@@ -1197,6 +1287,7 @@ def closeEvent(self, event: QCloseEvent) -> None: # noqa: N802 (Qt override) |
1197 | 1287 | Args: |
1198 | 1288 | event (QCloseEvent): The Qt close event. |
1199 | 1289 | """ |
| 1290 | + self.dock_panel() |
1200 | 1291 | self._save_cfg() |
1201 | 1292 | if self._pm is not None: |
1202 | 1293 | self._pm.restore_display_profiles(wrapup=True, wait=True) |
|
0 commit comments