* Fix lopsided cursor highlight on network panel header actions The QR and speed-test header buttons used raw Button components with asymmetric horizontal/vertical padding, producing a highlight box wider than tall around their near-square icons. Swap them for PanelActionButton, the canonical square inline-action component already used elsewhere in the kit (e.g. connectPwBtn), which sizes itself symmetrically around the icon. Fixes #8782 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Center icons in PanelActionButton on whole pixels A fractional size and anchors.centerIn put the glyph on half-pixel coordinates, so it rounded a pixel off the center of the fill: the speed-test icon sat low in the network header. Round the size, make it odd so odd-width glyphs get even margins, and place the icon on whole pixels. Co-authored-by: Fabian Behrendt <7931874+fabianbehrendt@users.noreply.github.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Fabian Behrendt <7931874+fabianbehrendt@users.noreply.github.com>
106 lines
3.8 KiB
QML
106 lines
3.8 KiB
QML
import QtQuick
|
||
import qs.Commons
|
||
import qs.Commons as Commons
|
||
|
||
// Small (22×22 by default) icon button used at the right edge of panel rows
|
||
// for inline actions — forget network, confirm passphrase, unpair device,
|
||
// etc. Two visual modes are supported via `hoverColor`:
|
||
// - default: hoverColor === foreground → subtle foreground-tint hover
|
||
// - urgent: hoverColor === bar.urgent → red-tint hover for destructive
|
||
// actions like forget/unpair
|
||
//
|
||
// `enabled` gates clicks and dims the icon. The component owns its own
|
||
// hover state visuals; mouse hover does NOT update any panel cursor state
|
||
// here because action buttons are not cursor targets — the row they live
|
||
// in is.
|
||
//
|
||
// Set `focusable: true` to make the button keyboard-tabbable with the
|
||
// shared hover-cursor/focus tokens. Use this
|
||
// in form contexts where Tab walks a list
|
||
// of controls; leave it false for the right-edge actions on panel rows
|
||
// where the row's CursorSurface owns the keyboard cursor.
|
||
//
|
||
// Set `hasCursor: true` to have the button render the same hover state as
|
||
// mouse hover — so a panel's keyboard cursor lands on it identically.
|
||
// Use this when a PanelActionButton is itself the cursor target (rather
|
||
// than living inside a CursorSurface row). Emits `hovered(bool)` on
|
||
// pointer enter/leave so the panel can update its cursor state to match.
|
||
BorderSurface {
|
||
id: root
|
||
|
||
property string iconText: ""
|
||
property string tooltipText: ""
|
||
property color foreground: Commons.Color.foreground
|
||
property color hoverColor: foreground
|
||
property string fontFamily: Style.font.family
|
||
property real fontSize: Style.font.icon
|
||
// Whole and odd, so an icon glyph (usually an odd number of pixels wide)
|
||
// has the same space on both sides instead of one pixel more on one side.
|
||
property real size: Math.round(Math.max(Style.space(22), fontSize + Style.spacing.sm * 2)) | 1
|
||
|
||
property bool focusable: false
|
||
property bool hasCursor: false
|
||
property bool bordered: false
|
||
|
||
signal clicked()
|
||
signal hovered(bool isHovered)
|
||
|
||
activeFocusOnTab: focusable
|
||
Keys.onReturnPressed: if (focusable) root.clicked()
|
||
Keys.onEnterPressed: if (focusable) root.clicked()
|
||
Keys.onSpacePressed: if (focusable) root.clicked()
|
||
|
||
implicitWidth: size
|
||
implicitHeight: size
|
||
radius: Style.cornerRadius
|
||
|
||
readonly property bool _showFocusRing: focusable && activeFocus
|
||
readonly property bool _hot: (mouse.containsMouse || root.hasCursor) && root.enabled
|
||
readonly property var _borderSpec: _showFocusRing
|
||
? Border.controlSpec("focus", hoverColor, hoverColor)
|
||
: (_hot && bordered
|
||
? Border.controlSpec("hover-cursor", hoverColor, hoverColor)
|
||
: (bordered ? Border.controlSpec("normal", foreground, Commons.Color.accent) : Border.none()))
|
||
|
||
color: _showFocusRing
|
||
? Style.focusFillFor(hoverColor, hoverColor)
|
||
: (_hot
|
||
? Style.hoverFillFor(hoverColor, hoverColor)
|
||
: "transparent")
|
||
borderSpec: _borderSpec
|
||
|
||
Behavior on color { ColorAnimation { duration: Style.duration(60) } }
|
||
|
||
Text {
|
||
textFormat: Text.PlainText
|
||
// Snap to whole pixels so the glyph doesn't round away from the fill's center.
|
||
x: Math.round((root.width - width) / 2)
|
||
y: Math.round((root.height - height) / 2)
|
||
text: root.iconText
|
||
color: root.enabled
|
||
? (root._hot ? root.hoverColor : root.foreground)
|
||
: Qt.darker(root.foreground, 2.0)
|
||
font.family: root.fontFamily
|
||
font.pixelSize: root.fontSize
|
||
}
|
||
|
||
MouseArea {
|
||
id: mouse
|
||
anchors.fill: parent
|
||
hoverEnabled: true
|
||
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||
enabled: root.enabled
|
||
onContainsMouseChanged: root.hovered(containsMouse)
|
||
onClicked: {
|
||
if (root.focusable) root.forceActiveFocus()
|
||
root.clicked()
|
||
}
|
||
}
|
||
|
||
PanelToolTip {
|
||
visible: root.tooltipText !== "" && mouse.containsMouse
|
||
text: root.tooltipText
|
||
fontFamily: root.fontFamily
|
||
}
|
||
}
|