wip card on hover

This commit is contained in:
2026-05-29 16:52:38 +02:00
parent 2042fbb357
commit 4e85fb6a74
2 changed files with 116 additions and 2 deletions
@@ -71,6 +71,7 @@ class BeamlineStatePill(BECWidget, QWidget):
}
_SETTINGS_FIELD_WIDTH = 280
_VALID_DRAG_PAYLOAD_MODES = {"config", "device"}
_VALID_CARD_BACKGROUND_MODES = {"hover", "always"}
MIME_PAYLOAD = "application/x-bec-beamline-state-payload"
MIME_CONFIG = "application/x-bec-beamline-state-config"
MIME_NAME = "application/x-bec-beamline-state-name"
@@ -100,6 +101,7 @@ class BeamlineStatePill(BECWidget, QWidget):
self._flash_active = False
self._expanded = False
self._hovered = False
self._card_background_mode = "hover"
self._drag_payload_mode = "config"
self._drag_start_position: QPoint | None = None
self._drag_started = False
@@ -361,6 +363,31 @@ class BeamlineStatePill(BECWidget, QWidget):
"""Set the payload mode used for drag-and-drop."""
self.drag_payload_mode = mode
@property
def card_background_mode(self) -> str:
"""
Background mode for the pill card.
Supported values:
``"hover"``: idle pills are transparent; hover/expanded pills use a card background.
``"always"``: idle pills keep the status-tinted background.
"""
return self._card_background_mode
@card_background_mode.setter
def card_background_mode(self, mode: str) -> None:
if mode not in self._VALID_CARD_BACKGROUND_MODES:
valid_modes = ", ".join(sorted(self._VALID_CARD_BACKGROUND_MODES))
raise ValueError(
f"Invalid card background mode '{mode}'. Expected one of: {valid_modes}."
)
self._card_background_mode = mode
self._apply_visual_state()
def set_card_background_mode(self, mode: str) -> None:
"""Set when the pill card background should be shown."""
self.card_background_mode = mode
def _refresh_latest_state(self) -> None:
if self._state_name is None:
return
@@ -411,8 +438,17 @@ class BeamlineStatePill(BECWidget, QWidget):
accent = colors["accent"]
on_accent = colors["on_accent"]
active_card = self._hovered or self._expanded
border = colors["flash_border"] if self._flash_active else colors["border"]
background = colors["flash_background"] if self._flash_active else colors["background"]
show_idle_background = self._card_background_mode == "always" or self._flash_active
border = (
colors["flash_border"]
if self._flash_active
else colors["border"] if show_idle_background else "transparent"
)
background = (
colors["flash_background"]
if self._flash_active
else colors["background"] if show_idle_background else "transparent"
)
if active_card:
background = (
"qlineargradient("
@@ -1109,6 +1145,8 @@ class BeamlineStateManager(BECWidget, QWidget):
USER_ACCESS = [
"drag_payload_mode",
"set_drag_payload_mode",
"card_background_mode",
"set_card_background_mode",
"refresh_states",
"clear_filters",
"remove",
@@ -1124,6 +1162,7 @@ class BeamlineStateManager(BECWidget, QWidget):
config: ConnectionConfig | None = None,
gui_id: str | None = None,
drag_payload_mode: str = "config",
card_background_mode: str = "hover",
**kwargs,
) -> None:
super().__init__(
@@ -1137,7 +1176,9 @@ class BeamlineStateManager(BECWidget, QWidget):
self._device_filter_text = ""
self._hidden_expanded = False
self._drag_payload_mode = "config"
self._card_background_mode = "hover"
self.drag_payload_mode = drag_payload_mode
self.card_background_mode = card_background_mode
self._empty_label = QLabel(
"No beamline states available.\n Add new state from toolbar or CLI.", self
@@ -1209,6 +1250,32 @@ class BeamlineStateManager(BECWidget, QWidget):
"""Set the payload mode used for drag-and-drop."""
self.drag_payload_mode = mode
@property
def card_background_mode(self) -> str:
"""
Background mode used by pills in this manager.
Supported values:
``"hover"``: idle pills are transparent; hover/expanded pills use a card background.
``"always"``: idle pills keep the status-tinted background.
"""
return self._card_background_mode
@card_background_mode.setter
def card_background_mode(self, mode: str) -> None:
if mode not in BeamlineStatePill._VALID_CARD_BACKGROUND_MODES:
valid_modes = ", ".join(sorted(BeamlineStatePill._VALID_CARD_BACKGROUND_MODES))
raise ValueError(
f"Invalid card background mode '{mode}'. Expected one of: {valid_modes}."
)
self._card_background_mode = mode
for pill in self._state_pills.values():
pill.card_background_mode = mode
def set_card_background_mode(self, mode: str) -> None:
"""Set when pill card backgrounds should be shown."""
self.card_background_mode = mode
def _create_toolbar(self) -> ModularToolBar:
toolbar = ModularToolBar(parent=self)
@@ -1358,6 +1425,7 @@ class BeamlineStateManager(BECWidget, QWidget):
parent=self._content, state_name=name, title=title, client=self.client
)
pill.drag_payload_mode = self._drag_payload_mode
pill.card_background_mode = self._card_background_mode
pill.set_state_config(state_config)
pill.state_changed.connect(self._on_pill_state_changed)
pill.update_requested.connect(self._update_state_parameters)
@@ -1587,6 +1655,12 @@ if __name__ == "__main__": # pragma: no cover
mode_combo.currentIndexChanged.connect(
lambda: setattr(manager, "drag_payload_mode", mode_combo.currentData())
)
background_combo = QComboBox(window)
background_combo.addItem("Card on hover", "hover")
background_combo.addItem("Card always", "always")
background_combo.currentIndexChanged.connect(
lambda: setattr(manager, "card_background_mode", background_combo.currentData())
)
drop_list = BeamlineStateDropList(window)
drop_list.setMinimumWidth(260)
@@ -1594,6 +1668,8 @@ if __name__ == "__main__": # pragma: no cover
payload_row = QHBoxLayout()
payload_row.addWidget(QLabel("Drag payload", window))
payload_row.addWidget(mode_combo)
payload_row.addWidget(QLabel("Card background", window))
payload_row.addWidget(background_combo)
payload_row.addStretch(1)
layout.addLayout(payload_row)
@@ -97,6 +97,7 @@ def test_beamline_state_pill_uses_card_style_when_expanded(qtbot, mocked_client)
qtbot.addWidget(widget)
assert "qlineargradient" not in widget.styleSheet()
assert "#BeamlineStatePill {background: transparent" in widget.styleSheet()
widget._toggle_expanded()
@@ -104,6 +105,18 @@ def test_beamline_state_pill_uses_card_style_when_expanded(qtbot, mocked_client)
assert widget._shadow.isEnabled()
def test_beamline_state_pill_can_keep_idle_background(qtbot, mocked_client):
widget = BeamlineStatePill(state_name="limits", title="Limits", client=mocked_client)
qtbot.addWidget(widget)
assert "#BeamlineStatePill {background: transparent" in widget.styleSheet()
widget.card_background_mode = "always"
assert "#BeamlineStatePill {background: transparent" not in widget.styleSheet()
assert "qlineargradient" not in widget.styleSheet()
def test_beamline_state_pill_uses_card_style_when_hovered(qtbot, mocked_client):
widget = BeamlineStatePill(state_name="limits", title="Limits", client=mocked_client)
qtbot.addWidget(widget)
@@ -208,6 +221,31 @@ def test_beamline_state_manager_propagates_drag_payload_mode(qtbot, mocked_clien
assert widget._state_pills["limits"].drag_payload_mode == "config"
def test_beamline_state_manager_propagates_card_background_mode(qtbot, mocked_client):
widget = BeamlineStateManager(client=mocked_client, card_background_mode="always")
qtbot.addWidget(widget)
widget.update_available_states(
{
"states": [
{
"name": "limits",
"title": "Limits",
"state_type": "DeviceWithinLimitsState",
"parameters": {"device": "samx"},
}
]
},
{},
)
assert widget._state_pills["limits"].card_background_mode == "always"
widget.card_background_mode = "hover"
assert widget._state_pills["limits"].card_background_mode == "hover"
def test_beamline_state_manager_filters_status(qtbot, mocked_client):
widget = BeamlineStateManager(client=mocked_client)
qtbot.addWidget(widget)