Files
arthur-os/default/quickshell/omarchy-shell/Widgets/NComboBox.qml
T
Jake Weaver ae70db3c53 Restyle Quickshell settings inputs to match Omarchy aesthetic
Settings dialogs were rendering text/spin/combo controls with Qt's
default light style, which clashed with the dark Omarchy theme — black
text on dark fills, white dropdown popups, rounded corners, and a
pill-style toggle that didn't match the native StyleToggleRow switch.

- Style.qml: collapse all radius tokens to 0 so Noctalia-compat widgets
  inherit Omarchy's sharp-corner convention
- NToggle: rewrite to mirror the native 48x22 switch with accent fill
  and a sliding 16x16 thumb
- NComboBox: skin contentItem, indicator, popup, and delegate so the
  dropdown reads on dark themes
- NSpinBox: add a styled background, contentItem, and up/down indicators
- SettingsPanel calendar form + DynamicSettingsForm string field: drop
  default TextField chrome for the same translucent foreground fill used
  by WidgetCard, accent border on focus
2026-05-15 16:46:56 -04:00

139 lines
3.6 KiB
QML

import QtQuick
import QtQuick.Controls
import qs.Commons
ComboBox {
id: root
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
implicitHeight: 32
leftPadding: 10
rightPadding: 28
topPadding: 4
bottomPadding: 4
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)
}
contentItem: Text {
text: root.displayText
color: Color.mOnSurface
font: root.font
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
color: Color.mSurfaceVariant
border.color: root.activeFocus ? Color.mPrimary : Color.mOutline
border.width: Style.borderS
radius: 0
}
indicator: Text {
x: root.width - width - 10
y: (root.height - height) / 2
text: "▾"
color: Color.mOnSurfaceVariant
font.family: root.font.family
font.pixelSize: 11
}
delegate: ItemDelegate {
id: del
required property int index
required property var modelData
width: ListView.view ? ListView.view.width : root.width
height: 28
highlighted: root.highlightedIndex === del.index
contentItem: Text {
text: {
var m = del.modelData
if (m === null || m === undefined) return ""
if (root.textRole && m[root.textRole] !== undefined) return String(m[root.textRole])
return String(m)
}
color: Color.mOnSurface
font: root.font
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
color: del.highlighted || del.hovered
? Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.14)
: "transparent"
radius: 0
}
}
popup: Popup {
y: root.height
width: root.width
implicitHeight: Math.min(contentItem.implicitHeight + 2, 280)
padding: 1
background: Rectangle {
color: Color.mSurface
border.color: Color.mOutline
border.width: Style.borderS
radius: 0
}
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: root.popup.visible ? root.delegateModel : null
currentIndex: root.highlightedIndex
boundsBehavior: Flickable.StopAtBounds
ScrollIndicator.vertical: ScrollIndicator { }
}
}
}