feat: add vector icons for transport controls, implement marker hover effects, and update dialog positioning
This commit is contained in:
@@ -10,6 +10,10 @@ Dialog {
|
||||
standardButtons: Dialog.NoButton
|
||||
width: 300
|
||||
|
||||
// Anchor to the top-left of the window instead of the default centered position.
|
||||
x: 24
|
||||
y: 24
|
||||
|
||||
// called by WaferMapView's TapHandler
|
||||
property int sensorIndex: -1
|
||||
property string sensorLabel: ""
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.impl
|
||||
import QtQuick.Layouts
|
||||
import ISC
|
||||
|
||||
@@ -177,8 +178,6 @@ Item {
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
hoverEnabled: true
|
||||
text: "⏮"
|
||||
font.pixelSize: 24
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: skipStartBtn.pressed ? Theme.transportButtonHover
|
||||
@@ -188,12 +187,13 @@ Item {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
contentItem: IconImage {
|
||||
source: "../icons/prev.svg"
|
||||
width: 22; height: 22
|
||||
sourceSize.width: 22
|
||||
sourceSize.height: 22
|
||||
color: Theme.headingColor
|
||||
font: parent.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
onClicked: streamController.step(-1)
|
||||
}
|
||||
@@ -205,8 +205,6 @@ Item {
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
hoverEnabled: true
|
||||
text: "■"
|
||||
font.pixelSize: 22
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: reviewStopBtn.pressed ? Theme.transportButtonHover
|
||||
@@ -216,12 +214,13 @@ Item {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
contentItem: IconImage {
|
||||
source: "../icons/stop.svg"
|
||||
width: 20; height: 20
|
||||
sourceSize.width: 20
|
||||
sourceSize.height: 20
|
||||
color: Theme.headingColor
|
||||
font: parent.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
onClicked: streamController.stop()
|
||||
}
|
||||
@@ -233,8 +232,6 @@ Item {
|
||||
implicitWidth: 64
|
||||
implicitHeight: 56
|
||||
hoverEnabled: true
|
||||
text: streamController.playing ? "⏸" : "▶"
|
||||
font.pixelSize: 24
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: playPauseBtn.pressed ? Theme.transportButtonHover
|
||||
@@ -244,13 +241,13 @@ Item {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
contentItem: IconImage {
|
||||
source: streamController.playing ? "../icons/pause.svg" : "../icons/play.svg"
|
||||
width: 22; height: 22
|
||||
sourceSize.width: 22
|
||||
sourceSize.height: 22
|
||||
color: Theme.headingColor
|
||||
font: parent.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: parent.text === "▶" ? 4 : 0
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
onClicked: {
|
||||
if (streamController.playing) {
|
||||
@@ -268,8 +265,6 @@ Item {
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
hoverEnabled: true
|
||||
text: "⏭"
|
||||
font.pixelSize: 24
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
color: skipEndBtn.pressed ? Theme.transportButtonHover
|
||||
@@ -279,12 +274,13 @@ Item {
|
||||
ColorAnimation { duration: Theme.durationFast }
|
||||
}
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
contentItem: IconImage {
|
||||
source: "../icons/next.svg"
|
||||
width: 22; height: 22
|
||||
sourceSize.width: 22
|
||||
sourceSize.height: 22
|
||||
color: Theme.headingColor
|
||||
font: parent.font
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
onClicked: streamController.step(1)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,22 @@ Item {
|
||||
highColor: Theme.sensorHigh
|
||||
textColor: Theme.headingColor
|
||||
|
||||
// Grows the hovered marker smoothly; hoverScale setter triggers a repaint.
|
||||
hoverScale: 1.0 + 0.35 * hoverPulse
|
||||
property real hoverPulse: map.hoveredIndex >= 0 ? 1.0 : 0.0
|
||||
Behavior on hoverPulse {
|
||||
NumberAnimation { duration: 150; easing.type: Easing.OutQuad }
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
onPointChanged: {
|
||||
map.hoveredIndex = map.which_marker(hoverHandler.point.position.x, hoverHandler.point.position.y);
|
||||
}
|
||||
onHoveredChanged: if (!hoverHandler.hovered) map.hoveredIndex = -1
|
||||
cursorShape: map.hoveredIndex >= 0 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: ev => {
|
||||
var idx = map.which_marker(ev.position.x, ev.position.y);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m12 19-7-7 7-7" />
|
||||
<path d="M19 12H5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 262 B |
@@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m9 18 6-6-6-6" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 237 B |
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect x="14" y="3" width="5" height="18" rx="1" />
|
||||
<rect x="6" y="3" width="5" height="18" rx="1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 313 B |
@@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polygon points="6 3 20 12 6 21 6 3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 250 B |
@@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m15 18-6-6 6-6" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 238 B |
@@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect width="18" height="18" x="3" y="3" rx="2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 261 B |
@@ -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
|
||||
@@ -441,6 +476,14 @@ class WaferMapItem(QQuickPaintedItem):
|
||||
|
||||
def paint(self, painter: QPainter) -> None:
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user