feat: add vector icons for transport controls, implement marker hover effects, and update dialog positioning

This commit is contained in:
jack
2026-07-06 13:51:45 -07:00
parent 915720085b
commit 1989ba8e5a
10 changed files with 182 additions and 31 deletions
@@ -52,6 +52,8 @@ class WaferMapItem(QQuickPaintedItem):
sizeChanged = Signal()
thicknessChanged = Signal()
showThicknessChanged = Signal()
hoveredIndexChanged = Signal()
hoverScaleChanged = Signal()
def __init__(self, parent=None):
@@ -68,6 +70,10 @@ class WaferMapItem(QQuickPaintedItem):
self._thickness_data: list[float] = []
self._show_thickness: bool = False
self._thickness_heatmap:QImage | None = None
# Hover highlight: index of the marker under the pointer (-1 = none)
# and a 1.0->~1.5 grow factor driven by a QML Behavior for a smooth animation.
self._hovered_index: int = -1
self._hover_scale: float = 1.0
# Dark-theme color defaults (match Theme.qml tokens)
self._ring_color = QColor("#2A3441") # waferRingColor (toneBorder)
@@ -81,6 +87,12 @@ class WaferMapItem(QQuickPaintedItem):
self._markers: dict[int, tuple[int, int]] = {} # sensor index → (px, py)
self._marker_r: int = 4
self._heatmap: QImage | None = None
# Size the markers/heatmap were last computed against. On first activation
# (e.g. a freshly-created Loader item) width()/height() can still be 0/stale
# when `sensors` is first set, computing degenerate marker positions that
# never get retried. paint() re-checks this every frame so geometry that
# settles after property assignment still gets picked up.
self._last_draw_size: int = -1
self.widthChanged.connect(self._on_resize)
self.heightChanged.connect(self._on_resize)
@@ -213,6 +225,29 @@ class WaferMapItem(QQuickPaintedItem):
self.showThicknessChanged.emit()
self.update()
@Property(int, notify=hoveredIndexChanged)
def hoveredIndex(self) -> int:
return self._hovered_index
@hoveredIndex.setter
def hoveredIndex(self, val: int) -> None:
val = int(val)
if val == self._hovered_index:
return
self._hovered_index = val
self.hoveredIndexChanged.emit()
self.update()
@Property(float, notify=hoverScaleChanged)
def hoverScale(self) -> float:
return self._hover_scale
@hoverScale.setter
def hoverScale(self, val: float) -> None:
self._hover_scale = float(val)
self.hoverScaleChanged.emit()
self.update()
# Colour properties — QML can bind these to Theme tokens
@Property(QColor, notify=colorsChanged)
def ringColor(self) -> QColor: return self._ring_color
@@ -440,7 +475,15 @@ class WaferMapItem(QQuickPaintedItem):
# ── paint ─────────────────────────────────────────────────────────────
def paint(self, painter: QPainter) -> None:
ds = self._draw_size()
ds = self._draw_size()
if ds != self._last_draw_size:
# Geometry settled (or changed) since markers/heatmap were last built
# against a stale size — recompute now, at actual paint time.
self._last_draw_size = ds
self._compute_markers()
self._rebuild_heatmap()
self._rebuild_thickness()
r_px = int(ds / 2 - 4)
cx, cy = self._center()
@@ -544,10 +587,22 @@ class WaferMapItem(QQuickPaintedItem):
self._bands[i] if i < len(self._bands) else "in_range",
self._in_range_color,
)
is_hovered = i == self._hovered_index
marker_r = int(round(r * self._hover_scale)) if is_hovered else r
if is_hovered:
# Soft glow ring behind the marker, growing with hoverScale.
glow_r = int(round(marker_r * 1.9))
glow_color = QColor(color)
glow_color.setAlpha(70)
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(QBrush(glow_color))
painter.drawEllipse(px - glow_r, py - glow_r, 2 * glow_r, 2 * glow_r)
# Filled circle with thin dark outline for contrast over heatmap
painter.setPen(QPen(QColor(0, 0, 0, 100), 1))
painter.setBrush(QBrush(color))
painter.drawEllipse(px - r, py - r, 2 * r, 2 * r)
painter.drawEllipse(px - marker_r, py - marker_r, 2 * marker_r, 2 * marker_r)
if self._show_labels:
has_temp = i < len(self._values)