Pins the active connection to 2.4/5/6GHz, or leaves the choice automatic.
The section only appears on Wi-Fi when the network answers on more than one
band, or while a pin is in force so it stays clearable.
bin/omarchy-network-band reports and sets the band. It sets NetworkManager's
802-11-wireless.band rather than pinning a BSSID: NM 1.44+ accepts a third
band value so 5 and 6GHz can be pinned apart, and a band survives an AP
rotating its BSSIDs while leaving roaming between APs intact. It refuses a
band the network does not answer on, and restores the previous setting if
reassociation fails rather than leaving the machine offline.
Under Automatic the pills collapse and the header reads the live band
("WI-FI BAND: 2.4GHZ"); pinning reveals them and the header goes plain. The
height is animated, and the section stays mounted through the reconnect a
band change causes -- otherwise `kind` briefly stops being "wifi" and the
whole segment would tear down and rebuild.
Two fixes to the same panel fall out of this:
- Every stat row stays mounted and reads "--" until it has data, instead of
appearing a beat after the panel opens and shoving the rows below down.
- The speed test's Run button is scaled to match the band header's controls
and joins the keyboard cursor chain.
ToggleSwitch gains a settable trackHeight so a compact placement can render a
genuinely small switch instead of scaling one down onto fractional pixels.
Defaults are unchanged.
Claude-Session: https://claude.ai/code/session_01XcKqYe1n5bnRBuAZwikwsr
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
112 lines
4.4 KiB
QML
112 lines
4.4 KiB
QML
import QtQuick
|
|
import qs.Commons
|
|
|
|
// Bare on/off switch: a track with a sliding knob and no label. This is the
|
|
// switch `Toggle` parks at the end of its labeled row, factored out so panel
|
|
// headers and other compact controls render the identical thing.
|
|
//
|
|
// The caller owns the value: bind `checked` to real state and flip it in
|
|
// response to `toggled()`. Services that already track a desired state
|
|
// optimistically (see the Tailscale service's `_desired`) get an instant knob
|
|
// throw for free, because `checked` is already the optimistic value.
|
|
//
|
|
// `busy` marks an operation in flight and swallows further clicks, but leaves
|
|
// hover, cursor, and tooltips alone so the control does not flicker every time
|
|
// a background refresh runs.
|
|
//
|
|
// The cursor is a ring drawn outside the track rather than a state on the
|
|
// track itself: themes give normal chrome a stronger border than hover-cursor
|
|
// (0.4 vs 0.25 by default), which is right for controls that are borderless at
|
|
// rest but would make a bordered track go *fainter* under the cursor. On the
|
|
// panel background the ring reads immediately. `cursorRing` follows
|
|
// `interactive` — a switch whose surrounding row owns the click owns the
|
|
// cursor too.
|
|
//
|
|
// `rounded` auto-detects from Style.cornerRadius so the switch follows the
|
|
// theme: pill shape when Hyprland corners are rounded, square on sharp.
|
|
Item {
|
|
id: root
|
|
|
|
property bool checked: false
|
|
property bool busy: false
|
|
|
|
// Off when the surrounding row owns the click, as in `Toggle`.
|
|
property bool interactive: true
|
|
|
|
// Panel-cursor flag. Same role as Button.hasCursor: panels with their own
|
|
// keyboard cursor bind this to drive the highlight separately from hover.
|
|
property bool hasCursor: false
|
|
|
|
property bool cursorRing: interactive
|
|
property int cursorPad: Style.space(6)
|
|
property bool rounded: Style.cornerRadius > 0
|
|
property color foreground: Color.foreground
|
|
property color accent: Color.accent
|
|
|
|
signal toggled()
|
|
signal hovered(bool isHovered)
|
|
|
|
readonly property alias containsMouse: mouse.containsMouse
|
|
readonly property bool hot: hasCursor || mouse.containsMouse
|
|
|
|
// `trackHeight` is settable so a compact placement — a switch riding a panel
|
|
// section header, say — can ask for a genuinely smaller control instead of
|
|
// scaling a big one down, which lands the track and knob on fractional pixels
|
|
// and blurs their edges. The derived sizes only carry floors low enough to
|
|
// stay out of an override's way; at the default track height each one is
|
|
// already above its floor, so nothing about the normal switch changes.
|
|
property int trackHeight: Math.max(22, Math.round(Style.spacing.controlHeight * 0.55))
|
|
property int trackWidth: Math.round(trackHeight * 1.9)
|
|
property int knobSize: Math.max(6, Math.round(trackHeight * 0.72))
|
|
property int knobInset: Math.max(1, Math.round((trackHeight - knobSize) / 2))
|
|
|
|
readonly property int _pad: cursorRing ? cursorPad : 0
|
|
|
|
implicitWidth: trackWidth + _pad * 2
|
|
implicitHeight: trackHeight + _pad * 2
|
|
|
|
BorderSurface {
|
|
anchors.fill: parent
|
|
visible: root.cursorRing && root.hot
|
|
color: "transparent"
|
|
radius: Style.cornerRadius
|
|
borderSpec: Border.controlSpec("hover-cursor", root.foreground, root.accent)
|
|
}
|
|
|
|
BorderSurface {
|
|
id: track
|
|
width: root.trackWidth
|
|
height: root.trackHeight
|
|
anchors.centerIn: parent
|
|
radius: root.rounded ? height / 2 : 0
|
|
color: root.checked
|
|
? Style.selectedFillFor(root.foreground, root.accent)
|
|
: Style.normalFillFor(root.foreground, root.accent)
|
|
borderSpec: Border.controlSpec(root.checked ? "selected" : "normal", root.foreground, root.accent)
|
|
|
|
Behavior on color { ColorAnimation { duration: 120 } }
|
|
|
|
Rectangle {
|
|
width: root.knobSize
|
|
height: root.knobSize
|
|
radius: root.rounded ? height / 2 : 0
|
|
x: root.checked ? track.width - width - root.knobInset : root.knobInset
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
color: root.checked ? Style.selectedStateColor(root.foreground, root.accent) : Qt.darker(root.foreground, 1.25)
|
|
|
|
Behavior on x { NumberAnimation { duration: 120; easing.type: Easing.OutCubic } }
|
|
Behavior on color { ColorAnimation { duration: 120 } }
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: mouse
|
|
anchors.fill: parent
|
|
enabled: root.interactive
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onContainsMouseChanged: root.hovered(containsMouse)
|
|
onClicked: if (!root.busy) root.toggled()
|
|
}
|
|
}
|