New tests for what the port added: status tint priority and context-dependent chip filters on the dewar/queue model, the right-button wheel guard (motor protection), PopoutWindow edge maths, manual-resize fallback, close-hides behavior and DockTitleBar, the chip drag-drop relabeling, and the console-log pop-out mirror. The widgets' __main__ self-checks are superseded by these and removed. Brings PR diff coverage from 70% to 81% (gate: 80%). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
172 lines
5.3 KiB
Python
172 lines
5.3 KiB
Python
"""PopoutWindow replaces dock floating: an additional top-level window whose
|
|
edge band resizes, whose close only hides, and whose first show lands near
|
|
the cursor. DockTitleBar puts the pop-out button next to the close box."""
|
|
|
|
from PySide6.QtCore import QEvent, QPoint, QPointF, Qt
|
|
from PySide6.QtGui import QMouseEvent
|
|
from PySide6.QtWidgets import QDockWidget, QLabel
|
|
|
|
from aare.gui.widgets.popout_window import DockTitleBar, PopoutWindow
|
|
|
|
|
|
def _window(qtbot):
|
|
w = PopoutWindow("test", QLabel("content"))
|
|
qtbot.addWidget(w)
|
|
w.resize(400, 300)
|
|
return w
|
|
|
|
|
|
def test_edges_at(qtbot):
|
|
w = _window(qtbot)
|
|
band = PopoutWindow.RESIZE_MARGIN + PopoutWindow.OUTER_GRIP
|
|
assert w._edges_at(QPoint(band - 1, 150)) == Qt.Edge.LeftEdge
|
|
assert w._edges_at(QPoint(200, band - 1)) == Qt.Edge.TopEdge
|
|
assert w._edges_at(QPoint(399, 299)) == (Qt.Edge.RightEdge | Qt.Edge.BottomEdge)
|
|
assert w._edges_at(QPoint(200, 150)) == Qt.Edge(0)
|
|
|
|
|
|
def test_cursor_for(qtbot):
|
|
w = _window(qtbot)
|
|
assert w._cursor_for(Qt.Edge.LeftEdge) == Qt.CursorShape.SizeHorCursor
|
|
assert w._cursor_for(Qt.Edge.BottomEdge) == Qt.CursorShape.SizeVerCursor
|
|
assert w._cursor_for(Qt.Edge.LeftEdge | Qt.Edge.TopEdge) == Qt.CursorShape.SizeFDiagCursor
|
|
assert w._cursor_for(Qt.Edge.RightEdge | Qt.Edge.TopEdge) == Qt.CursorShape.SizeBDiagCursor
|
|
assert w._cursor_for(Qt.Edge(0)) is None
|
|
|
|
|
|
def test_close_hides_and_keeps_geometry(qtbot):
|
|
w = _window(qtbot)
|
|
w.show()
|
|
qtbot.waitExposed(w)
|
|
w.move(120, 130)
|
|
geometry = w.geometry()
|
|
w.close()
|
|
assert not w.isVisible()
|
|
# Reopen: the first-show placement must not run again (geometry kept).
|
|
w.show()
|
|
assert w._placed
|
|
assert w.geometry() == geometry
|
|
|
|
|
|
def test_manual_resize_fallback(qtbot):
|
|
w = _window(qtbot)
|
|
w.show()
|
|
qtbot.waitExposed(w)
|
|
w.resize(400, 300)
|
|
start = w.geometry()
|
|
|
|
grab = QPointF(399.0, 299.0) # bottom-right band
|
|
press = QMouseEvent(
|
|
QEvent.Type.MouseButtonPress,
|
|
grab,
|
|
w.mapToGlobal(grab.toPoint()).toPointF(),
|
|
Qt.MouseButton.LeftButton,
|
|
Qt.MouseButton.LeftButton,
|
|
Qt.KeyboardModifier.NoModifier,
|
|
)
|
|
w.mousePressEvent(press)
|
|
if not w._manual_edges:
|
|
# The platform accepted startSystemResize; the manual path is not
|
|
# reachable here, and the press must not have moved the window.
|
|
assert w.geometry() == start
|
|
return
|
|
|
|
move_to = grab + QPointF(40.0, 25.0)
|
|
move = QMouseEvent(
|
|
QEvent.Type.MouseMove,
|
|
move_to,
|
|
w.mapToGlobal(move_to.toPoint()).toPointF(),
|
|
Qt.MouseButton.NoButton,
|
|
Qt.MouseButton.LeftButton,
|
|
Qt.KeyboardModifier.NoModifier,
|
|
)
|
|
w.mouseMoveEvent(move)
|
|
assert w.width() == start.width() + 40
|
|
assert w.height() == start.height() + 25
|
|
|
|
release = QMouseEvent(
|
|
QEvent.Type.MouseButtonRelease,
|
|
move_to,
|
|
w.mapToGlobal(move_to.toPoint()).toPointF(),
|
|
Qt.MouseButton.LeftButton,
|
|
Qt.MouseButton.NoButton,
|
|
Qt.KeyboardModifier.NoModifier,
|
|
)
|
|
w.mouseReleaseEvent(release)
|
|
assert not w._manual_edges
|
|
|
|
|
|
def test_hover_sets_resize_cursor(qtbot):
|
|
w = _window(qtbot)
|
|
w.show()
|
|
qtbot.waitExposed(w)
|
|
|
|
def hover(pos):
|
|
w.mouseMoveEvent(
|
|
QMouseEvent(
|
|
QEvent.Type.MouseMove,
|
|
pos,
|
|
w.mapToGlobal(pos.toPoint()).toPointF(),
|
|
Qt.MouseButton.NoButton,
|
|
Qt.MouseButton.NoButton,
|
|
Qt.KeyboardModifier.NoModifier,
|
|
)
|
|
)
|
|
|
|
hover(QPointF(2.0, 150.0))
|
|
assert w.cursor().shape() == Qt.CursorShape.SizeHorCursor
|
|
hover(QPointF(200.0, 150.0))
|
|
assert w.cursor().shape() == Qt.CursorShape.ArrowCursor
|
|
|
|
|
|
def test_dock_title_bar_buttons(qtbot):
|
|
dock = QDockWidget("Console Log")
|
|
qtbot.addWidget(dock)
|
|
opened = []
|
|
bar = DockTitleBar(dock, on_popout=lambda: opened.append(True))
|
|
dock.setTitleBarWidget(bar)
|
|
dock.show()
|
|
qtbot.waitExposed(dock)
|
|
|
|
qtbot.mouseClick(bar.popout_button, Qt.MouseButton.LeftButton)
|
|
assert opened == [True]
|
|
assert dock.isVisible()
|
|
|
|
|
|
def test_manual_resize_from_top_left(qtbot):
|
|
w = _window(qtbot)
|
|
w.show()
|
|
qtbot.waitExposed(w)
|
|
w.resize(400, 300)
|
|
# Drive the manual path directly; whether startSystemResize is available
|
|
# is platform luck and the top/left arithmetic deserves coverage either way.
|
|
w._manual_edges = Qt.Edge.LeftEdge | Qt.Edge.TopEdge
|
|
w._press_global = w.mapToGlobal(QPoint(2, 2))
|
|
w._press_geom = w.geometry()
|
|
move_to = QPointF(2.0 + 30.0, 2.0 + 20.0)
|
|
w.mouseMoveEvent(
|
|
QMouseEvent(
|
|
QEvent.Type.MouseMove,
|
|
move_to,
|
|
w.mapToGlobal(move_to.toPoint()).toPointF(),
|
|
Qt.MouseButton.NoButton,
|
|
Qt.MouseButton.LeftButton,
|
|
Qt.KeyboardModifier.NoModifier,
|
|
)
|
|
)
|
|
assert w.width() == 400 - 30
|
|
assert w.height() == 300 - 20
|
|
|
|
|
|
def test_paint_border_knob(qtbot, monkeypatch):
|
|
from PySide6.QtGui import QPixmap
|
|
|
|
from aare.gui.widgets import popout_window as mod
|
|
|
|
w = _window(qtbot)
|
|
# Default FRAME_L1_WIDTH "0px": render must take the paint-nothing path.
|
|
w.render(QPixmap(w.size()))
|
|
# With a visible level-1 border the painter path runs.
|
|
monkeypatch.setattr(mod, "FRAME_L1_WIDTH", "2px")
|
|
w.render(QPixmap(w.size()))
|