Promote bar/common to qs.Ui kit module
This commit is contained in:
@@ -10,7 +10,7 @@ import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import "common" as BarCommon
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -1716,7 +1716,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
BarCommon.PopupCard {
|
||||
PopupCard {
|
||||
id: managePopup
|
||||
anchorItem: trayRoot
|
||||
owner: trayRoot
|
||||
@@ -1801,7 +1801,7 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
BarCommon.PillButton {
|
||||
PillButton {
|
||||
id: rowPinBtn
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
@@ -1815,7 +1815,7 @@ Item {
|
||||
onClicked: trayRoot.togglePin(rowRoot.itemId)
|
||||
}
|
||||
|
||||
BarCommon.PillButton {
|
||||
PillButton {
|
||||
id: rowHideBtn
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: rowPinBtn.left
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
// Shared visual chrome for keyboard-and-mouse-navigable items inside a panel.
|
||||
// Contract: items must NOT read `containsMouse` for color/border. Mouse
|
||||
// hover updates the panel's cursor state at the root; visuals derive from
|
||||
// `hasCursor` / `current`. That's what guarantees a single highlight on
|
||||
// screen at any time across both keyboard and mouse interaction.
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool hasCursor: false
|
||||
property bool current: false
|
||||
|
||||
property color foreground: "#cacccc"
|
||||
property color fill: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.18)
|
||||
|
||||
radius: 4
|
||||
color: (hasCursor || current) ? fill : "transparent"
|
||||
border.width: hasCursor ? 1 : 0
|
||||
border.color: foreground
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 60 }
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
|
||||
// Layer-shell popup attached to a bar widget icon, designed for
|
||||
// click-driven AND keyboard-driven panels (e.g. SUPER+CTRL+W summon).
|
||||
//
|
||||
// Built on PanelWindow with WlrKeyboardFocus.Exclusive rather than
|
||||
// PopupWindow (xdg-popup). Layer-shell surfaces declared Exclusive get
|
||||
// keyboard focus from Hyprland *at map time*, which is the protocol-level
|
||||
// equivalent of focus-on-launch for xdg-toplevels. xdg-popups don't get
|
||||
// that — they only receive keys after a click/hover routes focus through
|
||||
// their parent surface — so keyboard-summoned popups fell flat without it.
|
||||
//
|
||||
// API is a subset of Common.PopupCard: anchorItem, owner, bar, open,
|
||||
// padding, margin, contentWidth/Height, default contentItem. Missing on
|
||||
// purpose (for now): centerOnBar, triggerMode ("hover"), containsMouse.
|
||||
// Hover-mode popups (system-stats, weather-flyout) and centered popups
|
||||
// (calendar week-view) need extra plumbing before migrating; converting
|
||||
// them is a follow-up.
|
||||
//
|
||||
// Positioning: full-screen layer-shell with the card placed inside at
|
||||
// `cardOrigin`. We use the bar window's height/width for the perpendicular
|
||||
// axis (away-from-bar) because mapToItem on the anchor returns
|
||||
// bar-content-relative coords with internal layout offsets baked in
|
||||
// (e.g. ~13px from the bar's vertical centering of its widget row). The
|
||||
// parallel axis (along-the-bar) uses the anchor's content x/y since the
|
||||
// bar spans full screen on that axis.
|
||||
//
|
||||
// Outside-click dismissal: an overlay MouseArea catches clicks, with the
|
||||
// QsWindow.mask subtracting the bar strip so clicks on the bar still
|
||||
// reach the bar widgets (activePopout coordinator hands off to another
|
||||
// popup if the user clicks a different bar icon).
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
required property Item anchorItem
|
||||
required property QtObject bar
|
||||
property var owner: null
|
||||
property int margin: 10
|
||||
property int padding: 14
|
||||
property int contentWidth: 280
|
||||
property int contentHeight: 200
|
||||
property bool open: false
|
||||
property int gap: 10 // distance between bar edge and panel
|
||||
|
||||
default property alias contentItem: contentHolder.children
|
||||
|
||||
readonly property var coordinatorKey: owner || root
|
||||
readonly property var anchorWindow: anchorItem ? anchorItem.QsWindow.window : null
|
||||
readonly property string barPos: bar ? bar.position : "top"
|
||||
|
||||
function closePopout() {
|
||||
if (owner && "closePopout" in owner) owner.closePopout()
|
||||
else root.open = false
|
||||
}
|
||||
|
||||
// --- screen + lifetime ---------------------------------------------------
|
||||
|
||||
screen: anchorWindow ? anchorWindow.screen : null
|
||||
visible: open || card.opacity > 0
|
||||
color: "transparent"
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
WlrLayershell.namespace: "omarchy-keyboard-panel"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
// Keyboard focus follows `open` (NOT `visible`). The window remains
|
||||
// mapped during the fade-out so the opacity animation has something to
|
||||
// animate, but keyboard/click ownership must release the moment the
|
||||
// logical close fires — otherwise the user is locked out for 140ms.
|
||||
WlrLayershell.keyboardFocus: open ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
|
||||
// Full-screen layer-shell. The visible card is positioned inside via
|
||||
// `cardOrigin`. The `mask` below makes the bar area click-through (so
|
||||
// the user can click another bar icon while the panel is open and the
|
||||
// activePopout coordinator swaps to that popup); everywhere else, the
|
||||
// overlay catches the click and dismisses via the MouseArea below.
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
// Clickable region = whole screen MINUS the bar's strip. Clicks on the
|
||||
// bar pass through to the bar layer; clicks anywhere else are caught
|
||||
// by us and either land on the card (no-op) or trigger dismissal.
|
||||
readonly property real _barStripSize: bar ? bar.barSize : 0
|
||||
mask: Region {
|
||||
width: root.screenW
|
||||
height: root.screenH
|
||||
Region {
|
||||
x: root.barPos === "right" ? root.screenW - root._barStripSize : 0
|
||||
y: root.barPos === "bottom" ? root.screenH - root._barStripSize : 0
|
||||
width: (root.barPos === "top" || root.barPos === "bottom") ? root.screenW : root._barStripSize
|
||||
height: (root.barPos === "top" || root.barPos === "bottom") ? root._barStripSize : root.screenH
|
||||
intersection: Intersection.Subtract
|
||||
}
|
||||
}
|
||||
|
||||
// Track every layout change between the bar's contentItem and the
|
||||
// anchor item. `transform` updates whenever any item in that chain
|
||||
// moves/resizes, which is what makes the position binding below
|
||||
// actually reactive — mapToItem on its own is a one-shot.
|
||||
TransformWatcher {
|
||||
id: anchorWatcher
|
||||
a: anchorWindow ? anchorWindow.contentItem : null
|
||||
b: anchorItem
|
||||
}
|
||||
|
||||
// Anchor item's position within the bar's content surface. For a
|
||||
// full-width top bar, the content x maps directly to screen x; the y
|
||||
// returned here has the bar's internal padding baked in (e.g. ~13px
|
||||
// from vertical centering of the widget row), which is why `cardOrigin`
|
||||
// below uses `barH` for the perpendicular axis instead of this y.
|
||||
readonly property point anchorScreenPos: {
|
||||
anchorWatcher.transform // reactive dependency
|
||||
if (!anchorItem || !anchorWindow) return Qt.point(0, 0)
|
||||
return anchorItem.mapToItem(anchorWindow.contentItem, 0, 0)
|
||||
}
|
||||
readonly property real anchorW: anchorItem ? anchorItem.width : 0
|
||||
readonly property real anchorH: anchorItem ? anchorItem.height : 0
|
||||
readonly property real screenW: screen ? screen.width : 0
|
||||
readonly property real screenH: screen ? screen.height : 0
|
||||
|
||||
// Desired top-left of the card in screen coordinates. For the
|
||||
// perpendicular axis (away-from-bar) we anchor to the bar window's edge
|
||||
// directly — not the anchor item's y/x — because mapToItem(barContent)
|
||||
// returns coordinates in the bar's content space, which can be offset
|
||||
// from the bar surface's screen-anchored corner by internal layout
|
||||
// (centering wrappers, padding). The bar's surface IS aligned to its
|
||||
// anchored screen edge, so using `barW`/`barH` gives the right edge
|
||||
// regardless of how the bar's internal widgets are positioned. For the
|
||||
// parallel axis (along the bar) the anchor item's reported position is
|
||||
// still consistent with the bar content origin, so it's accurate for
|
||||
// centering the card under the icon.
|
||||
readonly property real barW: anchorWindow ? anchorWindow.width : screenW
|
||||
readonly property real barH: anchorWindow ? anchorWindow.height : 0
|
||||
readonly property point cardOrigin: {
|
||||
if (!anchorItem || !bar) return Qt.point(margin, margin)
|
||||
var x = 0, y = 0
|
||||
if (barPos === "bottom") {
|
||||
x = anchorScreenPos.x + anchorW / 2 - contentWidth / 2
|
||||
y = screenH - barH - contentHeight - gap
|
||||
} else if (barPos === "left") {
|
||||
x = barW + gap
|
||||
y = anchorScreenPos.y + anchorH / 2 - contentHeight / 2
|
||||
} else if (barPos === "right") {
|
||||
x = screenW - barW - contentWidth - gap
|
||||
y = anchorScreenPos.y + anchorH / 2 - contentHeight / 2
|
||||
} else { // "top" (default)
|
||||
x = anchorScreenPos.x + anchorW / 2 - contentWidth / 2
|
||||
y = barH + gap
|
||||
}
|
||||
x = Math.max(margin, Math.min(x, screenW - contentWidth - margin))
|
||||
y = Math.max(margin, Math.min(y, screenH - contentHeight - margin))
|
||||
return Qt.point(Math.round(x), Math.round(y))
|
||||
}
|
||||
|
||||
|
||||
// --- popout coordination (same-bar single-popout model) -----------------
|
||||
|
||||
// Coordinate on `open`, not `visible`. `visible` lags into the fade-out
|
||||
// animation, which made ownership transfer to a sibling popup race.
|
||||
onOpenChanged: {
|
||||
if (!bar) return
|
||||
if (open) bar.requestPopout(coordinatorKey)
|
||||
else if (bar.activePopout === coordinatorKey) bar.releasePopout(coordinatorKey)
|
||||
}
|
||||
|
||||
// --- outside-click dismissal --------------------------------------------
|
||||
|
||||
// Catches clicks anywhere in the clickable region (i.e. everywhere on
|
||||
// screen except the bar strip, which is masked out). The card has its
|
||||
// own MouseArea below so clicks on it don't bubble up here. Disabled
|
||||
// during the fade-out so the dying overlay doesn't swallow clicks that
|
||||
// were meant for the apps behind it.
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.open
|
||||
onClicked: root.closePopout()
|
||||
}
|
||||
|
||||
// --- card ----------------------------------------------------------------
|
||||
|
||||
Rectangle {
|
||||
id: card
|
||||
x: root.cardOrigin.x
|
||||
y: root.cardOrigin.y
|
||||
width: root.contentWidth
|
||||
height: root.contentHeight
|
||||
color: Color.popups.background
|
||||
border.color: Color.popups.border
|
||||
border.width: 2
|
||||
radius: 0
|
||||
opacity: root.open ? 1.0 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
// Swallow clicks on the card so they don't bubble to the dismissal
|
||||
// MouseArea behind us.
|
||||
MouseArea { anchors.fill: parent }
|
||||
|
||||
Item {
|
||||
id: contentHolder
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.padding
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
// 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.
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string iconText: ""
|
||||
property string tooltipText: ""
|
||||
property color foreground: "#cacccc"
|
||||
property color hoverColor: foreground
|
||||
property color panelBackground: "#101315"
|
||||
property string fontFamily: "JetBrainsMono Nerd Font"
|
||||
property real fontSize: 14
|
||||
property real size: 22
|
||||
|
||||
signal clicked()
|
||||
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
radius: 4
|
||||
|
||||
color: mouse.containsMouse && root.enabled
|
||||
? Qt.rgba(hoverColor.r, hoverColor.g, hoverColor.b, 0.20)
|
||||
: "transparent"
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 60 } }
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: root.iconText
|
||||
color: root.enabled
|
||||
? (mouse.containsMouse ? root.hoverColor : Qt.darker(root.foreground, 1.3))
|
||||
: 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
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
PanelToolTip {
|
||||
visible: root.tooltipText !== "" && mouse.containsMouse
|
||||
text: root.tooltipText
|
||||
panelForeground: root.foreground
|
||||
panelBackground: root.panelBackground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
// Small-caps-style label that introduces a panel section ("DNS provider",
|
||||
// "Wi-Fi networks", "Output device", "Paired devices"). Sits between a
|
||||
// PanelSeparator and the content rows.
|
||||
Text {
|
||||
id: root
|
||||
|
||||
property color foreground: "#cacccc"
|
||||
property string fontFamily: "JetBrainsMono Nerd Font"
|
||||
property real fontSize: 10
|
||||
|
||||
color: Qt.darker(foreground, 1.4)
|
||||
font.family: fontFamily
|
||||
font.pixelSize: fontSize
|
||||
font.bold: true
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
// 1px horizontal divider for panel sections. The alpha-on-foreground tint
|
||||
// keeps the rule legible against the panel background without competing
|
||||
// with text or borders.
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property color foreground: "#cacccc"
|
||||
property real strength: 0.12
|
||||
|
||||
width: parent ? parent.width : implicitWidth
|
||||
implicitWidth: 100
|
||||
implicitHeight: 1
|
||||
height: 1
|
||||
color: Qt.rgba(foreground.r, foreground.g, foreground.b, strength)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
// Styled wrapper around Qt Quick Controls ToolTip. Drop-in: declare inside
|
||||
// the hovered item and bind `visible` to the hover state, e.g.
|
||||
// Common.PanelToolTip {
|
||||
// visible: mouse.containsMouse
|
||||
// text: "Forget network"
|
||||
// panelForeground: bar.foreground
|
||||
// panelBackground: bar.background
|
||||
// fontFamily: bar.fontFamily
|
||||
// }
|
||||
//
|
||||
// Property names are prefixed `panel*` to avoid clashing with ToolTip's
|
||||
// built-in `background`/`font` properties.
|
||||
ToolTip {
|
||||
id: root
|
||||
|
||||
property color panelForeground: "#cacccc"
|
||||
property color panelBackground: "#101315"
|
||||
property string fontFamily: "JetBrainsMono Nerd Font"
|
||||
property real fontSize: 11
|
||||
|
||||
delay: 400
|
||||
padding: 0
|
||||
|
||||
background: Rectangle {
|
||||
color: root.panelBackground
|
||||
border.color: root.panelForeground
|
||||
border.width: 1
|
||||
radius: 0
|
||||
opacity: 0.97
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
text: root.text
|
||||
color: root.panelForeground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.fontSize
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
topPadding: 6
|
||||
bottomPadding: 6
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string text: ""
|
||||
property string iconText: ""
|
||||
property string tooltipText: ""
|
||||
property color foreground: "#cacccc"
|
||||
property color background: "transparent"
|
||||
property color hoverBackground: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.12)
|
||||
property color pressedBackground: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.22)
|
||||
property color tooltipBackground: "#101315"
|
||||
property color tooltipForeground: foreground
|
||||
property string fontFamily: "JetBrainsMono Nerd Font"
|
||||
property real fontSize: 12
|
||||
property real iconSize: 14
|
||||
property real horizontalPadding: 10
|
||||
property real verticalPadding: 6
|
||||
property bool active: false
|
||||
property bool leftAlign: false
|
||||
property color activeBackground: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.18)
|
||||
|
||||
// Keyboard cursor flag. When true, the pill renders the same fill + border
|
||||
// as a mouse hover — so j/k navigation and pointer hover are visually
|
||||
// indistinguishable. Default false; bind from a panel's cursor state.
|
||||
property bool hasCursor: false
|
||||
|
||||
ToolTip {
|
||||
visible: root.tooltipText !== "" && mouseArea.containsMouse
|
||||
text: root.tooltipText
|
||||
delay: 400
|
||||
padding: 0
|
||||
|
||||
background: Rectangle {
|
||||
color: root.tooltipBackground
|
||||
border.color: root.tooltipForeground
|
||||
border.width: 1
|
||||
radius: 0
|
||||
opacity: 0.97
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
text: root.tooltipText
|
||||
color: root.tooltipForeground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: 11
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
topPadding: 6
|
||||
bottomPadding: 6
|
||||
}
|
||||
}
|
||||
|
||||
signal clicked()
|
||||
signal rightClicked()
|
||||
|
||||
implicitWidth: row.implicitWidth + horizontalPadding * 2
|
||||
implicitHeight: row.implicitHeight + verticalPadding * 2
|
||||
radius: 4
|
||||
|
||||
// Hot = mouse hover OR keyboard cursor. Pressed wins over both; otherwise
|
||||
// hot beats `active` (so a cursor on an already-active pill still reads
|
||||
// as hovered, matching CursorSurface's behaviour for navigable rows).
|
||||
readonly property bool hot: mouseArea.containsMouse || hasCursor
|
||||
|
||||
color: mouseArea.pressed ? pressedBackground
|
||||
: hot ? hoverBackground
|
||||
: (active ? activeBackground : background)
|
||||
border.width: hot ? 1 : 0
|
||||
border.color: foreground
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 120 }
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: root.leftAlign ? parent.left : undefined
|
||||
anchors.leftMargin: root.leftAlign ? root.horizontalPadding : 0
|
||||
anchors.horizontalCenter: root.leftAlign ? undefined : parent.horizontalCenter
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
visible: root.iconText !== ""
|
||||
text: root.iconText
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.iconSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: root.text !== ""
|
||||
text: root.text
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.fontSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: function(mouse) {
|
||||
if (mouse.button === Qt.RightButton) root.rightClicked()
|
||||
else root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.Commons
|
||||
|
||||
PopupWindow {
|
||||
id: root
|
||||
|
||||
required property Item anchorItem
|
||||
required property QtObject bar
|
||||
property var owner: null
|
||||
property int margin: 10
|
||||
property int padding: 14
|
||||
property int contentWidth: 280
|
||||
property int contentHeight: 200
|
||||
property bool open: false
|
||||
property bool centerOnBar: false
|
||||
// "click" — uses HyprlandFocusGrab so clicking outside dismisses the popup.
|
||||
// "hover" — passive overlay; the owning widget controls open via hover.
|
||||
property string triggerMode: "click"
|
||||
|
||||
readonly property var coordinatorKey: owner || root
|
||||
readonly property var anchorWindow: anchorItem ? anchorItem.QsWindow.window : null
|
||||
readonly property bool containsMouse: cardHover.hovered
|
||||
|
||||
function closePopout() {
|
||||
if (owner && "closePopout" in owner) owner.closePopout()
|
||||
else root.open = false
|
||||
}
|
||||
|
||||
default property alias contentItem: contentHolder.children
|
||||
|
||||
visible: open || card.opacity > 0
|
||||
color: "transparent"
|
||||
implicitWidth: contentWidth
|
||||
implicitHeight: contentHeight
|
||||
|
||||
onOpenChanged: {
|
||||
if (!bar) return
|
||||
if (open) bar.requestPopout(coordinatorKey)
|
||||
else if (bar.activePopout === coordinatorKey) bar.releasePopout(coordinatorKey)
|
||||
}
|
||||
|
||||
// Outside-click dismissal via Hyprland's focus grab. While `active`, input
|
||||
// is routed only to the listed windows; clicking anywhere else clears the
|
||||
// grab and we close the popup. Skipped for hover-mode popups so the cursor
|
||||
// can move freely between the trigger and the popup.
|
||||
HyprlandFocusGrab {
|
||||
active: root.open && root.triggerMode === "click"
|
||||
windows: root.anchorWindow ? [root, root.anchorWindow] : [root]
|
||||
onCleared: root.closePopout()
|
||||
}
|
||||
|
||||
anchor {
|
||||
id: popupAnchor
|
||||
window: anchorItem ? anchorItem.QsWindow.window : null
|
||||
adjustment: PopupAdjustment.Slide
|
||||
edges: Edges.Top | Edges.Left
|
||||
gravity: Edges.Bottom | Edges.Right
|
||||
rect.width: 1
|
||||
rect.height: 1
|
||||
|
||||
onAnchoring: {
|
||||
if (!root.anchorItem || !root.bar) return
|
||||
|
||||
var target = root.anchorItem
|
||||
var popupWidth = root.implicitWidth
|
||||
var popupHeight = root.implicitHeight
|
||||
var localX = target.width / 2 - popupWidth / 2
|
||||
var localY = target.height + root.margin
|
||||
|
||||
if (root.bar.position === "bottom") {
|
||||
localY = -popupHeight - root.margin
|
||||
} else if (root.bar.position === "left") {
|
||||
localX = target.width + root.margin
|
||||
localY = target.height / 2 - popupHeight / 2
|
||||
} else if (root.bar.position === "right") {
|
||||
localX = -popupWidth - root.margin
|
||||
localY = target.height / 2 - popupHeight / 2
|
||||
}
|
||||
|
||||
var window = target.QsWindow.window
|
||||
if (!window) return
|
||||
|
||||
if (root.centerOnBar) {
|
||||
var cx = 0;
|
||||
var cy = 0;
|
||||
if (root.bar.position === "top" || root.bar.position === "bottom") {
|
||||
cx = window.width / 2 - popupWidth / 2
|
||||
cy = root.bar.position === "bottom" ? -popupHeight - root.margin : window.height + root.margin
|
||||
cx = Math.max(root.margin, Math.min(cx, window.width - popupWidth - root.margin))
|
||||
} else {
|
||||
cx = root.bar.position === "left" ? window.width + root.margin : -popupWidth - root.margin
|
||||
cy = window.height / 2 - popupHeight / 2
|
||||
cy = Math.max(root.margin, Math.min(cy, window.height - popupHeight - root.margin))
|
||||
}
|
||||
|
||||
popupAnchor.rect.x = Math.round(cx)
|
||||
popupAnchor.rect.y = Math.round(cy)
|
||||
return
|
||||
}
|
||||
|
||||
var point = window.contentItem.mapFromItem(target, localX, localY)
|
||||
|
||||
if (root.bar.position === "top" || root.bar.position === "bottom") {
|
||||
point.x = Math.max(root.margin, Math.min(point.x, window.width - popupWidth - root.margin))
|
||||
} else {
|
||||
point.y = Math.max(root.margin, Math.min(point.y, window.height - popupHeight - root.margin))
|
||||
}
|
||||
|
||||
popupAnchor.rect.x = Math.round(point.x)
|
||||
popupAnchor.rect.y = Math.round(point.y)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: card
|
||||
anchors.fill: parent
|
||||
color: Color.popups.background
|
||||
border.color: Color.popups.border
|
||||
border.width: 2
|
||||
radius: 0
|
||||
opacity: root.open ? 1.0 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentHolder
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.padding
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: cardHover
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property QtObject bar: null
|
||||
property real value: 0
|
||||
property real minimum: 0
|
||||
property real maximum: 1
|
||||
property real step: 0.05
|
||||
property bool integer: false
|
||||
property color trackColor: bar ? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.18) : "#333"
|
||||
property color fillColor: bar ? bar.foreground : "#cacccc"
|
||||
property color knobColor: bar ? bar.foreground : "#cacccc"
|
||||
property bool dragging: false
|
||||
property real trackHeight: 4
|
||||
property real liveValue: value
|
||||
|
||||
onValueChanged: if (!dragging) liveValue = value
|
||||
|
||||
signal moved(real value)
|
||||
signal released(real value)
|
||||
|
||||
implicitWidth: 200
|
||||
implicitHeight: 22
|
||||
|
||||
readonly property real range: Math.max(0.0001, maximum - minimum)
|
||||
readonly property real progress: Math.max(0, Math.min(1, (liveValue - minimum) / range))
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: root.trackHeight
|
||||
radius: height / 2
|
||||
color: root.trackColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: fill
|
||||
anchors.verticalCenter: track.verticalCenter
|
||||
anchors.left: track.left
|
||||
height: track.height
|
||||
radius: track.radius
|
||||
color: root.fillColor
|
||||
width: track.width * root.progress
|
||||
|
||||
Behavior on width {
|
||||
enabled: !root.dragging
|
||||
NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: knob
|
||||
width: 14
|
||||
height: 14
|
||||
radius: 7
|
||||
color: root.knobColor
|
||||
border.color: root.bar ? root.bar.background : "#101315"
|
||||
border.width: 2
|
||||
anchors.verticalCenter: track.verticalCenter
|
||||
x: Math.max(0, Math.min(track.width - width, track.width * root.progress - width / 2))
|
||||
scale: mouseArea.containsMouse || root.dragging ? 1.15 : 1.0
|
||||
|
||||
Behavior on x {
|
||||
enabled: !root.dragging
|
||||
NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 110; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
|
||||
function valueFromX(x) {
|
||||
var clamped = Math.max(0, Math.min(track.width, x))
|
||||
var raw = root.minimum + (clamped / track.width) * root.range
|
||||
if (root.integer) raw = Math.round(raw)
|
||||
return Math.max(root.minimum, Math.min(root.maximum, raw))
|
||||
}
|
||||
|
||||
onPressed: function(mouse) {
|
||||
root.dragging = true
|
||||
var next = valueFromX(mouse.x)
|
||||
root.liveValue = next
|
||||
root.moved(next)
|
||||
}
|
||||
onPositionChanged: function(mouse) {
|
||||
if (!root.dragging) return
|
||||
var next = valueFromX(mouse.x)
|
||||
root.liveValue = next
|
||||
root.moved(next)
|
||||
}
|
||||
onReleased: function(mouse) {
|
||||
root.dragging = false
|
||||
root.released(root.liveValue)
|
||||
root.liveValue = root.value
|
||||
}
|
||||
onWheel: function(wheel) {
|
||||
var delta = wheel.angleDelta.y > 0 ? root.step : -root.step
|
||||
var next = Math.max(root.minimum, Math.min(root.maximum, root.liveValue + delta))
|
||||
if (root.integer) next = Math.round(next)
|
||||
root.liveValue = next
|
||||
root.moved(next)
|
||||
root.released(next)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var bar: null
|
||||
property string text: ""
|
||||
property string fontFamily: bar ? bar.fontFamily : "JetBrainsMono Nerd Font"
|
||||
property real fontSize: 12
|
||||
property color foreground: bar ? bar.foreground : "#cacccc"
|
||||
property color activeColor: bar ? bar.urgent : "#a55555"
|
||||
property bool active: false
|
||||
property real horizontalMargin: 8.5
|
||||
property real rightExtraMargin: 0
|
||||
property real verticalPadding: 6
|
||||
property real fixedWidth: -1
|
||||
property real fixedHeight: -1
|
||||
property real textRotation: 0
|
||||
property bool keepSpace: false
|
||||
property string tooltipText: ""
|
||||
|
||||
signal pressed(int button)
|
||||
signal wheelMoved(int delta)
|
||||
|
||||
readonly property bool vertical: bar ? bar.vertical : false
|
||||
readonly property int barSize: bar ? bar.barSize : 26
|
||||
|
||||
visible: text !== "" || keepSpace
|
||||
opacity: text === "" ? 0 : 1
|
||||
implicitWidth: fixedWidth > 0 ? fixedWidth : (vertical ? barSize : Math.max(12, label.implicitWidth + horizontalMargin * 2 + rightExtraMargin))
|
||||
implicitHeight: fixedHeight > 0 ? fixedHeight : (vertical ? Math.max(12, label.implicitHeight + verticalPadding * 2) : barSize)
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: root.vertical ? 0 : -root.rightExtraMargin / 2
|
||||
text: root.text
|
||||
color: root.active ? root.activeColor : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pointSize: root.fontSize * 0.75
|
||||
renderType: Text.NativeRendering
|
||||
rotation: root.textRotation
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 160 }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: if (root.bar) root.bar.showTooltip(root, root.tooltipText)
|
||||
onExited: if (root.bar) root.bar.hideTooltip(root)
|
||||
onClicked: function(mouse) {
|
||||
if (root.bar) root.bar.hideTooltip(root)
|
||||
root.pressed(mouse.button)
|
||||
}
|
||||
onWheel: function(wheel) { root.wheelMoved(wheel.angleDelta.y) }
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Pipewire
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -90,7 +90,7 @@ Item {
|
||||
// selectedIndex semantics within a section:
|
||||
// -1 → on the slider row (h/l adjusts volume, m/Enter mute)
|
||||
// 0..N-1 → on the Nth device/stream row
|
||||
// Visuals derive from hasCursor/current via Common.CursorSurface, never
|
||||
// Visuals derive from hasCursor/current via CursorSurface, never
|
||||
// from containsMouse — that's what keeps the highlight unique across
|
||||
// keyboard + mouse like wifi does.
|
||||
property string focusSection: "output"
|
||||
@@ -348,7 +348,7 @@ Item {
|
||||
function hide(): void { root.closePopout() }
|
||||
}
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
@@ -366,7 +366,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.KeyboardPanel {
|
||||
KeyboardPanel {
|
||||
id: panel
|
||||
anchorItem: button
|
||||
owner: root
|
||||
@@ -440,7 +440,7 @@ Item {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "Output"
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
@@ -462,7 +462,7 @@ Item {
|
||||
// Output slider row — itself a cursor target (selectedIndex === -1
|
||||
// when focusSection === "output"). h/l adjust the value via
|
||||
// root.adjustVolume; m / Enter toggle mute.
|
||||
Common.CursorSurface {
|
||||
CursorSurface {
|
||||
id: outputSliderRow
|
||||
width: parent.width
|
||||
height: outputSliderInner.implicitHeight + 8
|
||||
@@ -496,7 +496,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.Slider {
|
||||
PanelSlider {
|
||||
id: outputSlider
|
||||
bar: root.bar
|
||||
width: parent.width - outputIconText.width - outputPercent.width - 16
|
||||
@@ -559,7 +559,7 @@ Item {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "Input"
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
@@ -578,7 +578,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.CursorSurface {
|
||||
CursorSurface {
|
||||
id: inputSliderRow
|
||||
visible: !!root.source
|
||||
width: parent.width
|
||||
@@ -613,7 +613,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.Slider {
|
||||
PanelSlider {
|
||||
id: inputSlider
|
||||
bar: root.bar
|
||||
width: parent.width - inputIconText.width - inputPercent.width - 16
|
||||
@@ -672,7 +672,7 @@ Item {
|
||||
spacing: 6
|
||||
visible: root.audioStreams.length > 0
|
||||
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "Playing"
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
@@ -701,7 +701,7 @@ Item {
|
||||
// Output device row — cursor target inside the "output" section. Mouse
|
||||
// hover updates the panel cursor at the root; visuals come entirely
|
||||
// from hasCursor/current via CursorSurface, never from containsMouse.
|
||||
component SinkRow: Common.CursorSurface {
|
||||
component SinkRow: CursorSurface {
|
||||
id: sinkRow
|
||||
required property var node
|
||||
required property int rowIndex
|
||||
@@ -767,7 +767,7 @@ Item {
|
||||
}
|
||||
|
||||
// Input device row — sibling of SinkRow for the "input" section.
|
||||
component SourceRow: Common.CursorSurface {
|
||||
component SourceRow: CursorSurface {
|
||||
id: sourceRow
|
||||
required property var node
|
||||
required property int rowIndex
|
||||
@@ -836,7 +836,7 @@ Item {
|
||||
// The stream has its own slider inline, so h/l from the keyboard
|
||||
// adjusts THIS stream's volume (not the global output) when the cursor
|
||||
// sits on this row. Enter/Space mutes the stream.
|
||||
component StreamRow: Common.CursorSurface {
|
||||
component StreamRow: CursorSurface {
|
||||
id: streamRow
|
||||
required property var node
|
||||
required property int rowIndex
|
||||
@@ -906,7 +906,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.Slider {
|
||||
PanelSlider {
|
||||
bar: root.bar
|
||||
width: parent.width
|
||||
minimum: 0
|
||||
|
||||
@@ -3,7 +3,7 @@ import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Bluetooth
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -71,7 +71,7 @@ Item {
|
||||
// them, Enter activates.
|
||||
// "known" — paired/known device rows; Enter toggles connect.
|
||||
// "discovered" — unpaired devices visible while scanning; Enter pairs.
|
||||
// Visuals always come from Common.CursorSurface (hasCursor / current),
|
||||
// Visuals always come from CursorSurface (hasCursor / current),
|
||||
// never from containsMouse. Mouse hover updates root cursor state too,
|
||||
// guaranteeing one highlight on screen.
|
||||
property string focusSection: "header"
|
||||
@@ -310,7 +310,7 @@ Item {
|
||||
function hide(): void { root.closePopout() }
|
||||
}
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
@@ -322,7 +322,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.KeyboardPanel {
|
||||
KeyboardPanel {
|
||||
id: panel
|
||||
anchorItem: button
|
||||
owner: root
|
||||
@@ -445,7 +445,7 @@ Item {
|
||||
}
|
||||
|
||||
// Discovered (unpaired) devices, only shown while scanning.
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
visible: root.adapter && root.adapter.discovering && root.discoveredDevices.length > 0
|
||||
text: "Discovered"
|
||||
foreground: root.bar.foreground
|
||||
@@ -483,11 +483,11 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Header pill — wraps Common.PillButton with cursor-state binding so it
|
||||
// Header pill — wraps PillButton with cursor-state binding so it
|
||||
// participates in the same single-cursor model as device rows. We don't
|
||||
// use Common.CursorSurface here because the pill already provides its
|
||||
// use CursorSurface here because the pill already provides its
|
||||
// own active/hover visuals and we want them to layer correctly.
|
||||
component HeaderPill: Common.PillButton {
|
||||
component HeaderPill: PillButton {
|
||||
id: pill
|
||||
required property int pillIndex
|
||||
property bool pillEnabled: true
|
||||
@@ -528,7 +528,7 @@ Item {
|
||||
// connect that drops back to Disconnected within 10s surfaces as "Failed".
|
||||
// Now a cursor target: hasCursor binds to root state, mouse hover updates
|
||||
// root state. The X button on the right is a PanelActionButton.
|
||||
component DeviceRow: Common.CursorSurface {
|
||||
component DeviceRow: CursorSurface {
|
||||
id: row
|
||||
required property var dev
|
||||
required property int rowIndex
|
||||
@@ -660,7 +660,7 @@ Item {
|
||||
|
||||
// Explicit close button on any known device. Action depends on state:
|
||||
// connected -> disconnect, otherwise -> forget the pairing entirely.
|
||||
Common.PanelActionButton {
|
||||
PanelActionButton {
|
||||
id: disconnectBtn
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -57,7 +57,7 @@ Item {
|
||||
onDateChanged: root.now = clockTimer.date
|
||||
}
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
@@ -76,7 +76,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.PopupCard {
|
||||
PopupCard {
|
||||
id: popup
|
||||
anchorItem: button
|
||||
bar: root.bar
|
||||
@@ -94,7 +94,7 @@ Item {
|
||||
width: parent.width
|
||||
implicitHeight: 28
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
id: prevButton
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -114,7 +114,7 @@ Item {
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
id: nextButton
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -53,7 +53,7 @@ Item {
|
||||
implicitWidth: button.implicitWidth
|
||||
implicitHeight: button.implicitHeight
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
|
||||
@@ -2,7 +2,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -71,7 +71,7 @@ Item {
|
||||
implicitWidth: button.implicitWidth
|
||||
implicitHeight: button.implicitHeight
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Mpris
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -113,7 +113,7 @@ Item {
|
||||
onExited: if (root.bar) root.bar.hideTooltip(root)
|
||||
}
|
||||
|
||||
Common.PopupCard {
|
||||
PopupCard {
|
||||
id: popup
|
||||
anchorItem: root
|
||||
bar: root.bar
|
||||
@@ -198,7 +198,7 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 6
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
iconText: ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 10
|
||||
@@ -208,7 +208,7 @@ Item {
|
||||
onClicked: if (root.activePlayer) root.activePlayer.previous()
|
||||
}
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
iconText: root.activePlayer && root.activePlayer.isPlaying ? "" : ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 14
|
||||
@@ -219,7 +219,7 @@ Item {
|
||||
onClicked: if (root.activePlayer) root.activePlayer.togglePlaying()
|
||||
}
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
iconText: ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 10
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -36,7 +36,7 @@ Item {
|
||||
|
||||
PwObjectTracker { objects: root.source ? [root.source] : [] }
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
|
||||
@@ -2,7 +2,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -174,7 +174,7 @@ Item {
|
||||
onRunningChanged: if (!running) root.refresh()
|
||||
}
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
@@ -186,7 +186,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.PopupCard {
|
||||
PopupCard {
|
||||
anchorItem: button
|
||||
owner: root
|
||||
bar: root.bar
|
||||
@@ -222,7 +222,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Common.Slider {
|
||||
PanelSlider {
|
||||
id: brightnessSlider
|
||||
bar: root.bar
|
||||
width: parent.width - 22 - brightnessPercent.width - 16
|
||||
|
||||
@@ -2,7 +2,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -66,7 +66,7 @@ Item {
|
||||
// Single cursor model: exactly one highlighted spot across the whole
|
||||
// panel, located via `focusSection` + (`selectedIndex` | `dnsIndex`).
|
||||
// Mouse hover and keyboard nav both mutate this state at the root; items
|
||||
// never read containsMouse for visuals. See Common.CursorSurface for the
|
||||
// never read containsMouse for visuals. See CursorSurface for the
|
||||
// shared chrome (fill / border) shared by NetworkRow and DnsProviderPill.
|
||||
readonly property color activeFill: bar ? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.18) : "transparent"
|
||||
|
||||
@@ -518,7 +518,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
}
|
||||
}
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
@@ -535,12 +535,12 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
}
|
||||
|
||||
// Keyboard-driven popup anchored to the bar widget icon. The shared
|
||||
// Common.KeyboardPanel handles the layer-shell PanelWindow scaffolding
|
||||
// KeyboardPanel handles the layer-shell PanelWindow scaffolding
|
||||
// (Exclusive focus on map, screen binding, anchored-to-icon positioning,
|
||||
// outside-click via an overlay MouseArea + Region mask that lets the bar
|
||||
// remain clickable, fade animation, popout coordination). What stays
|
||||
// here is the wifi-specific UI inside.
|
||||
Common.KeyboardPanel {
|
||||
KeyboardPanel {
|
||||
id: panel
|
||||
anchorItem: button
|
||||
owner: root
|
||||
@@ -664,7 +664,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
}
|
||||
}
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
id: refreshBtn
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -681,7 +681,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
}
|
||||
}
|
||||
|
||||
Common.PanelSeparator {
|
||||
PanelSeparator {
|
||||
visible: !!root.info.iface
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
@@ -776,7 +776,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
}
|
||||
|
||||
// DNS provider selection.
|
||||
Common.PanelSeparator {
|
||||
PanelSeparator {
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
|
||||
@@ -784,7 +784,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "DNS provider"
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
@@ -825,12 +825,12 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
}
|
||||
|
||||
// Wi-Fi networks (only if a Wi-Fi station is available).
|
||||
Common.PanelSeparator {
|
||||
PanelSeparator {
|
||||
visible: root.wifiStationAvailable
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
visible: root.wifiStationAvailable
|
||||
text: root.scanning ? "Scanning Wi-Fi…" : "Wi-Fi networks"
|
||||
foreground: root.bar.foreground
|
||||
@@ -882,7 +882,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
// One DNS provider pill. The cursor + current visuals come entirely from
|
||||
// CursorSurface; this component just binds them to the panel's cursor
|
||||
// state and renders the label/tooltip/click target.
|
||||
component DnsProviderPill: Common.CursorSurface {
|
||||
component DnsProviderPill: CursorSurface {
|
||||
id: pill
|
||||
required property string provider
|
||||
required property int index
|
||||
@@ -921,7 +921,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
onClicked: pill.clicked()
|
||||
}
|
||||
|
||||
Common.PanelToolTip {
|
||||
PanelToolTip {
|
||||
visible: pill.tooltipText !== "" && pillMouse.containsMouse
|
||||
text: pill.tooltipText
|
||||
panelForeground: root.bar.foreground
|
||||
@@ -934,7 +934,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
// expands inline to a passphrase prompt when the user picks a protected
|
||||
// network we don't have credentials for. Clicking a connected row
|
||||
// disconnects; the X button on a connected row forgets the network.
|
||||
component NetworkRow: Common.CursorSurface {
|
||||
component NetworkRow: CursorSurface {
|
||||
id: row
|
||||
required property var net
|
||||
required property int index
|
||||
@@ -1042,7 +1042,7 @@ iwctl known-networks list 2>/dev/null \\
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Common.PanelActionButton {
|
||||
PanelActionButton {
|
||||
id: forgetBtn
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -1167,7 +1167,7 @@ iwctl known-networks list 2>/dev/null \\
|
||||
// 22×22 right-anchored to line up with forgetBtn and lockIndicator
|
||||
// above. Esc closes the prompt (handled by pwField.Keys.onEscapePressed)
|
||||
// so there's no separate cancel button.
|
||||
Common.PanelActionButton {
|
||||
PanelActionButton {
|
||||
id: connectPwBtn
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -2,7 +2,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -72,7 +72,7 @@ Item {
|
||||
implicitWidth: button.implicitWidth
|
||||
implicitHeight: button.implicitHeight
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
@@ -103,7 +103,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.PopupCard {
|
||||
PopupCard {
|
||||
id: popup
|
||||
anchorItem: button
|
||||
bar: root.bar
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -147,7 +147,7 @@ Item {
|
||||
onButtonHoveredChanged: buttonHovered ? showPopup() : scheduleHide()
|
||||
onPopupHoveredChanged: popupHovered ? hideTimer.stop() : scheduleHide()
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
@@ -169,7 +169,7 @@ Item {
|
||||
onHoveredChanged: root.buttonHovered = hovered
|
||||
}
|
||||
|
||||
Common.PopupCard {
|
||||
PopupCard {
|
||||
id: popup
|
||||
anchorItem: button
|
||||
owner: root
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../common" as Common
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -279,7 +279,7 @@ Item {
|
||||
onTriggered: root.refresh()
|
||||
}
|
||||
|
||||
Common.WidgetButton {
|
||||
WidgetButton {
|
||||
id: button
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -305,7 +305,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.PopupCard {
|
||||
PopupCard {
|
||||
id: popup
|
||||
anchorItem: button
|
||||
owner: root
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import "../bar/common" as Common
|
||||
import qs.Ui
|
||||
import qs.Commons
|
||||
|
||||
// Visual reference + live playground for omarchy-shell's common UI
|
||||
@@ -124,7 +124,7 @@ Item {
|
||||
font.bold: true
|
||||
}
|
||||
Text {
|
||||
text: "Live previews of every component in plugins/bar/common/. Use this as the visual reference when porting panels or extracting new primitives. Press h/l to walk the cursor demo, Esc to close."
|
||||
text: "Live previews of every type exported from qs.Ui. Use this as the visual reference when porting panels or building plugins. Press h/l to walk the cursor demo, Esc to close."
|
||||
color: Qt.darker(root.foreground, 1.4)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: 11
|
||||
@@ -133,7 +133,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.PanelSeparator { foreground: root.foreground }
|
||||
PanelSeparator { foreground: root.foreground }
|
||||
|
||||
// ---- PanelSectionHeader ------------------------------------------
|
||||
Column {
|
||||
@@ -171,17 +171,17 @@ Item {
|
||||
anchors.rightMargin: 14
|
||||
spacing: 6
|
||||
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "DNS provider"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "Wi-Fi networks"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "Playing"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
@@ -227,9 +227,9 @@ Item {
|
||||
anchors.rightMargin: 14
|
||||
spacing: 12
|
||||
|
||||
Common.PanelSeparator { foreground: root.foreground }
|
||||
Common.PanelSeparator { foreground: root.foreground; strength: 0.25 }
|
||||
Common.PanelSeparator { foreground: root.foreground; strength: 0.45 }
|
||||
PanelSeparator { foreground: root.foreground }
|
||||
PanelSeparator { foreground: root.foreground; strength: 0.25 }
|
||||
PanelSeparator { foreground: root.foreground; strength: 0.45 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,7 +279,7 @@ Item {
|
||||
{ "label": "Forget / scan / disabled-ish row" }
|
||||
]
|
||||
|
||||
Common.CursorSurface {
|
||||
CursorSurface {
|
||||
required property var modelData
|
||||
required property int index
|
||||
width: parent.width
|
||||
@@ -351,7 +351,7 @@ Item {
|
||||
anchors.leftMargin: 14
|
||||
spacing: 6
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
text: "DHCP"
|
||||
tooltipText: "Use DNS from DHCP"
|
||||
tooltipBackground: root.background
|
||||
@@ -360,7 +360,7 @@ Item {
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
text: "Cloudflare"
|
||||
tooltipText: "Set DNS to Cloudflare"
|
||||
tooltipBackground: root.background
|
||||
@@ -370,7 +370,7 @@ Item {
|
||||
active: true
|
||||
}
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
iconText: ""
|
||||
tooltipText: "Refresh"
|
||||
tooltipBackground: root.background
|
||||
@@ -381,7 +381,7 @@ Item {
|
||||
verticalPadding: 4
|
||||
}
|
||||
|
||||
Common.PillButton {
|
||||
PillButton {
|
||||
iconText: ""
|
||||
text: "On"
|
||||
tooltipText: "Turn Bluetooth off"
|
||||
@@ -431,7 +431,7 @@ Item {
|
||||
anchors.leftMargin: 14
|
||||
spacing: 18
|
||||
|
||||
Common.PanelActionButton {
|
||||
PanelActionButton {
|
||||
iconText: ""
|
||||
tooltipText: "Confirm (default flavor)"
|
||||
foreground: root.foreground
|
||||
@@ -439,7 +439,7 @@ Item {
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
|
||||
Common.PanelActionButton {
|
||||
PanelActionButton {
|
||||
iconText: ""
|
||||
tooltipText: "Forget network (urgent flavor)"
|
||||
foreground: root.foreground
|
||||
@@ -448,7 +448,7 @@ Item {
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
|
||||
Common.PanelActionButton {
|
||||
PanelActionButton {
|
||||
iconText: ""
|
||||
tooltipText: "Disabled — type a passphrase first"
|
||||
foreground: root.foreground
|
||||
@@ -504,7 +504,7 @@ Item {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Common.PanelToolTip {
|
||||
PanelToolTip {
|
||||
visible: tipMouse.containsMouse
|
||||
text: "Styled tooltip — drop into any panel"
|
||||
panelForeground: root.foreground
|
||||
@@ -563,7 +563,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Common.Slider {
|
||||
PanelSlider {
|
||||
id: demoSlider
|
||||
bar: root.fakeBar
|
||||
width: parent.width - 70
|
||||
@@ -623,13 +623,13 @@ Item {
|
||||
anchors.rightMargin: 14
|
||||
spacing: 6
|
||||
|
||||
Common.PanelSectionHeader {
|
||||
PanelSectionHeader {
|
||||
text: "Wi-Fi networks"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
|
||||
Common.CursorSurface {
|
||||
CursorSurface {
|
||||
width: parent.width
|
||||
implicitHeight: composedRow.implicitHeight + 12
|
||||
current: true
|
||||
@@ -655,7 +655,7 @@ Item {
|
||||
font.pixelSize: 14
|
||||
}
|
||||
|
||||
Common.PanelActionButton {
|
||||
PanelActionButton {
|
||||
id: composedForget
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -695,9 +695,9 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Common.PanelSeparator { foreground: root.foreground }
|
||||
PanelSeparator { foreground: root.foreground }
|
||||
|
||||
Common.CursorSurface {
|
||||
CursorSurface {
|
||||
width: parent.width
|
||||
implicitHeight: idleRow.implicitHeight + 12
|
||||
foreground: root.foreground
|
||||
|
||||
Reference in New Issue
Block a user