fix: window and dock sizing behavior
Three main-window chrome fixes: the un-maximized geometry is pre-set to half-screen centered so leaving maximized mode no longer restores an oversized size hint (the beamline state bar also stops imposing its ~2000px minimum width); closing a popped-out dock re-docks it instead of silently hiding the panel, and pop-outs open enlarged and clamped to the screen; state-bar buttons only update font/stylesheet/cursor when values change, since repolishing every DAQ tick dropped the hover cursor under a resting mouse. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -189,6 +189,15 @@ def main():
|
||||
|
||||
splash.set_progress(100, "Ready")
|
||||
splash.finish(win)
|
||||
# Pre-set the "normal" (un-maximized) geometry as a fraction of the
|
||||
# primary screen, centered — otherwise leaving maximized mode restores
|
||||
# the size hint, which is wider than the monitor. Other panels may
|
||||
# still enforce a somewhat larger minimum; the window then lands on
|
||||
# that minimum instead.
|
||||
unmax_w, unmax_h = 0.5, 0.7
|
||||
available = app.primaryScreen().availableGeometry()
|
||||
win.resize(int(available.width() * unmax_w), int(available.height() * unmax_h))
|
||||
win.move(available.center() - win.rect().center())
|
||||
# Maximized so the window adapts to the monitor instead of its size hint,
|
||||
# which is taller than a 1920x1200 console.
|
||||
win.showMaximized()
|
||||
|
||||
@@ -884,6 +884,13 @@ class MainWindow(QMainWindow):
|
||||
|
||||
register_tutorials(self, self.tutorial_manager)
|
||||
|
||||
# Every dock: closing its popped-out window brings it back into the
|
||||
# layout instead of hiding it (eventFilter), and popping out opens
|
||||
# an enlarged window (2x width, 3x height, clamped to the screen).
|
||||
for dock in self.findChildren(QDockWidget):
|
||||
dock.installEventFilter(self)
|
||||
dock.topLevelChanged.connect(self._on_dock_top_level_changed)
|
||||
|
||||
def _setup_global_shortcuts(self) -> None:
|
||||
self._shortcut_manual_sample = QAction("Expand Manual Sample", self)
|
||||
self._shortcut_manual_sample.setShortcut(QKeySequence("Ctrl+M"))
|
||||
@@ -2410,7 +2417,38 @@ class MainWindow(QMainWindow):
|
||||
def _mark_user_interaction(self) -> None:
|
||||
self._refresh_idle_activity(report_backend=True)
|
||||
|
||||
@Slot(bool)
|
||||
def _on_dock_top_level_changed(self, floating: bool) -> None:
|
||||
dock = self.sender()
|
||||
if floating and isinstance(dock, QDockWidget):
|
||||
# Pop-outs open enlarged instead of keeping the cramped docked
|
||||
# size. Deferred: the window is mid-reparent while the signal
|
||||
# fires.
|
||||
QTimer.singleShot(0, lambda d=dock: self._enlarge_floating_dock(d))
|
||||
|
||||
def _enlarge_floating_dock(self, dock: QDockWidget) -> None:
|
||||
if not dock.isFloating():
|
||||
return
|
||||
screen = dock.screen().availableGeometry()
|
||||
dock.resize(
|
||||
min(dock.width() * 2, int(screen.width() * 0.9)),
|
||||
min(dock.height() * 3, int(screen.height() * 0.9)),
|
||||
)
|
||||
# Keep the enlarged window fully on screen.
|
||||
geo = dock.frameGeometry()
|
||||
dx = min(0, screen.right() - geo.right())
|
||||
dy = min(0, screen.bottom() - geo.bottom())
|
||||
if dx or dy:
|
||||
dock.move(geo.x() + dx, geo.y() + dy)
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
# Closing a floated (popped-out) dock re-docks it instead of hiding —
|
||||
# otherwise the panel silently disappears and has to be restored via
|
||||
# the View menu.
|
||||
if event.type() == QEvent.Type.Close and isinstance(obj, QDockWidget) and obj.isFloating():
|
||||
obj.setFloating(False)
|
||||
event.ignore()
|
||||
return True
|
||||
try:
|
||||
if event.type() in {
|
||||
QEvent.Type.MouseButtonPress,
|
||||
|
||||
@@ -5,7 +5,13 @@ from PySide6.QtCore import Qt, QTimer, Signal, Slot
|
||||
from PySide6.QtGui import QCursor, QFont, QFontMetrics
|
||||
from PySide6.QtWidgets import QFrame, QHBoxLayout, QLabel, QMenu, QPushButton, QSizePolicy, QToolTip
|
||||
|
||||
from aare.gui.styles import STATE_AVAILABLE, STATE_MSG_ERROR, STATE_MSG_INFO, STATE_UNAVAILABLE
|
||||
from aare.gui.styles import (
|
||||
FONT_VALUE,
|
||||
STATE_AVAILABLE,
|
||||
STATE_MSG_ERROR,
|
||||
STATE_MSG_INFO,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
|
||||
# Shortcut transitions from the "Available transitions" menu in
|
||||
# widgets/status_bar.py show_state_menu — these come ON TOP of the one-hop
|
||||
@@ -154,7 +160,7 @@ class BeamlineStatePanel(QFrame):
|
||||
if index:
|
||||
separator = QLabel("–", self)
|
||||
separator.setStyleSheet(
|
||||
f"color: {STATE_UNAVAILABLE}; background: transparent; border: none; font-size: 18px;"
|
||||
f"color: {STATE_UNAVAILABLE}; background: transparent; border: none; font-size: {FONT_VALUE};"
|
||||
)
|
||||
layout.addWidget(separator)
|
||||
button = HoverableButton(label, self)
|
||||
@@ -178,6 +184,15 @@ class BeamlineStatePanel(QFrame):
|
||||
layout.addStretch(1)
|
||||
self._apply_highlight()
|
||||
|
||||
def minimumSizeHint(self):
|
||||
# The 13-button strip would otherwise impose a ~2000px minimum on the
|
||||
# whole main window, so an un-maximized window could never fit the
|
||||
# screen. Width 0: the strip adapts (two-line labels) and, below that,
|
||||
# clips — the window stays freely resizable.
|
||||
hint = super().minimumSizeHint()
|
||||
hint.setWidth(0)
|
||||
return hint
|
||||
|
||||
def resizeEvent(self, event) -> None:
|
||||
super().resizeEvent(event)
|
||||
self._update_label_mode()
|
||||
@@ -325,21 +340,31 @@ class BeamlineStatePanel(QFrame):
|
||||
font = QFont(self.font())
|
||||
font.setPixelSize(18)
|
||||
font.setWeight(QFont.Weight.Bold if bold else QFont.Weight.Normal)
|
||||
button.setFont(font)
|
||||
button.setStyleSheet(
|
||||
if button.font() != font:
|
||||
button.setFont(font)
|
||||
# Guarded updates: this runs on every DAQ tick, and re-applying an
|
||||
# unchanged stylesheet repolishes the button, which drops the hover
|
||||
# cursor under a resting mouse until it moves again.
|
||||
qss = (
|
||||
f"QPushButton {{ border: none; background: transparent; color: {color};"
|
||||
f" padding: 1px 8px; }}"
|
||||
f" QPushButton:hover {{ color: {color}; }}"
|
||||
)
|
||||
if button.styleSheet() != qss:
|
||||
button.setStyleSheet(qss)
|
||||
|
||||
# Clickability follows availability; unavailable states get the
|
||||
# forbidden cursor and only the deferred 3 s explanation tooltip.
|
||||
if is_available:
|
||||
button.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
button.setToolTip(self._TOOLTIPS.get(state, state.display_name()))
|
||||
cursor = Qt.CursorShape.PointingHandCursor
|
||||
tooltip = self._TOOLTIPS.get(state, state.display_name())
|
||||
else:
|
||||
button.setCursor(Qt.CursorShape.ForbiddenCursor)
|
||||
button.setToolTip("")
|
||||
cursor = Qt.CursorShape.ForbiddenCursor
|
||||
tooltip = ""
|
||||
if button.cursor().shape() != cursor:
|
||||
button.setCursor(cursor)
|
||||
if button.toolTip() != tooltip:
|
||||
button.setToolTip(tooltip)
|
||||
|
||||
def set_current_state(self, state: BeamlineStateEnum | None) -> None:
|
||||
self._current_state = state
|
||||
|
||||
Reference in New Issue
Block a user