Refine quickshell bar and Noctalia compatibility

This commit is contained in:
Ryan Hughes
2026-05-14 02:21:48 -04:00
parent 5086654d51
commit 7d2745b6a6
28 changed files with 1096 additions and 699 deletions
@@ -4,10 +4,57 @@ import qs.Commons
ComboBox {
id: root
property string label: ""
property string label: ""
// Noctalia's NComboBox API commonly uses a model of { key, name } objects,
// a currentKey property, and an onSelected(key, item) handler. Qt's ComboBox
// only knows currentIndex/currentText, so bridge the small API surface here.
property string currentKey: ""
signal selected(var key, var item)
textRole: "name"
valueRole: "key"
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: Style.fontSizeS
function itemAt(index) {
if (index < 0) return null
if (Array.isArray(root.model)) return root.model[index] || null
return null
}
function keyAt(index) {
var item = itemAt(index)
if (!item) return ""
if (item.key !== undefined && item.key !== null) return String(item.key)
if (item.value !== undefined && item.value !== null) return String(item.value)
if (item.name !== undefined && item.name !== null) return String(item.name)
return String(item)
}
function syncCurrentIndex() {
if (!Array.isArray(root.model)) return
for (var i = 0; i < root.model.length; i++) {
if (keyAt(i) === String(root.currentKey || "")) {
if (root.currentIndex !== i) root.currentIndex = i
return
}
}
if (root.model.length > 0 && root.currentIndex < 0) root.currentIndex = 0
}
Component.onCompleted: syncCurrentIndex()
onCurrentKeyChanged: syncCurrentIndex()
onModelChanged: syncCurrentIndex()
onActivated: function(index) {
var item = itemAt(index)
var key = keyAt(index)
root.currentKey = key
root.selected(key, item)
}
background: Rectangle {
color: Color.mSurfaceVariant
border.color: Color.mOutline
@@ -8,14 +8,18 @@ Menu {
id: root
property var model: []
signal triggered(string action)
// Noctalia passes the owning ShellScreen through here. Qt Quick Controls
// Menu doesn't need it, but accepting the property keeps plugin QML from
// failing at load time.
property var screen: null
signal triggered(var action, var item)
Instantiator {
model: root.model
delegate: MenuItem {
required property var modelData
text: modelData ? String(modelData.label || modelData.action || "") : ""
onTriggered: root.triggered(modelData ? String(modelData.action || "") : "")
onTriggered: root.triggered(modelData ? String(modelData.action || "") : "", modelData)
}
onObjectAdded: function(index, object) { root.insertItem(index, object) }
onObjectRemoved: function(index, object) { root.removeItem(object) }
@@ -0,0 +1,26 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
Rectangle {
id: root
property int currentIndex: 0
property int margins: Style.marginXS
property bool distributeEvenly: false
implicitHeight: 32
radius: Style.radiusS
color: Color.mSurfaceVariant
border.color: Color.mOutline
border.width: Style.borderS
default property alias content: row.children
RowLayout {
id: row
anchors.fill: parent
anchors.margins: root.margins
spacing: Style.marginXS
}
}
@@ -0,0 +1,46 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
Rectangle {
id: root
property string text: ""
property int tabIndex: -1
property bool checked: false
signal clicked()
Layout.fillWidth: true
implicitWidth: label.implicitWidth + Style.marginL * 2
implicitHeight: 26
radius: Style.radiusXS
color: area.containsMouse
? Color.mHover
: (checked ? Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.18) : "transparent")
border.color: checked ? Color.mPrimary : "transparent"
border.width: checked ? 1 : 0
Behavior on color { ColorAnimation { duration: Style.animationFast } }
Text {
id: label
anchors.centerIn: parent
text: root.text
color: checked ? Color.mPrimary : Color.mOnSurface
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: Style.fontSizeS
font.bold: checked
elide: Text.ElideRight
width: Math.max(0, parent.width - Style.marginM * 2)
horizontalAlignment: Text.AlignHCenter
}
MouseArea {
id: area
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clicked()
}
}
@@ -1,16 +1,26 @@
import QtQuick
import QtQuick.Controls
import qs.Commons
Switch {
Rectangle {
id: root
property string label: ""
indicator: Rectangle {
implicitWidth: 32
implicitHeight: 18
x: root.leftPadding
y: root.height / 2 - height / 2
property string label: ""
property string text: ""
property bool checked: false
signal toggled(var value)
signal clicked()
implicitWidth: labelItem.visible ? indicator.width + 8 + labelItem.implicitWidth : indicator.width
implicitHeight: Math.max(indicator.height, labelItem.implicitHeight)
color: "transparent"
opacity: enabled ? 1 : 0.4
Rectangle {
id: indicator
width: 32
height: 18
anchors.verticalCenter: parent.verticalCenter
radius: height / 2
color: root.checked ? Color.mPrimary : Color.mSurfaceVariant
border.color: Color.mOutline
@@ -27,12 +37,28 @@ Switch {
}
}
contentItem: Text {
leftPadding: 38
Text {
id: labelItem
visible: (root.label || root.text) !== ""
anchors.left: indicator.right
anchors.leftMargin: 8
anchors.verticalCenter: parent.verticalCenter
text: root.label || root.text
color: Color.mOnSurface
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: Style.fontSizeS
verticalAlignment: Text.AlignVCenter
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
enabled: root.enabled
cursorShape: Qt.PointingHandCursor
onClicked: {
root.checked = !root.checked
root.clicked()
root.toggled(root.checked)
}
}
}
@@ -13,3 +13,5 @@ NTextInput 1.0 NTextInput.qml
NComboBox 1.0 NComboBox.qml
NCheckbox 1.0 NCheckbox.qml
NDivider 1.0 NDivider.qml
NTabBar 1.0 NTabBar.qml
NTabButton 1.0 NTabButton.qml