add noctalia and fuzzel
This commit is contained in:
@@ -0,0 +1,342 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
property real minimumWidth: 200
|
||||
property real popupHeight: 180
|
||||
|
||||
property string label: ""
|
||||
property string description: ""
|
||||
property string tooltip: ""
|
||||
property var model
|
||||
property string currentKey: ""
|
||||
property string placeholder: ""
|
||||
property var defaultValue: undefined
|
||||
property string settingsPath: ""
|
||||
property real baseSize: 1.0
|
||||
|
||||
readonly property real preferredHeight: Math.round(Style.baseWidgetSize * 1.1 * root.baseSize)
|
||||
readonly property var comboBox: combo
|
||||
|
||||
signal selected(string key)
|
||||
|
||||
spacing: Style.marginL
|
||||
|
||||
// Less strict comparison with != (instead of !==) so it can properly compare int vs string (ex for FPS: 30 and "30")
|
||||
readonly property bool isValueChanged: (defaultValue !== undefined) && (currentKey != defaultValue)
|
||||
|
||||
readonly property string indicatorTooltip: {
|
||||
if (defaultValue === undefined)
|
||||
return "";
|
||||
var displayValue = "";
|
||||
if (defaultValue === "") {
|
||||
// Try to find the display name for empty key in the model
|
||||
var found = false;
|
||||
if (root.model) {
|
||||
if (Array.isArray(root.model)) {
|
||||
for (var i = 0; i < root.model.length; i++) {
|
||||
var item = root.model[i];
|
||||
if (item && item.key === "") {
|
||||
displayValue = item.name || I18n.tr("panels.indicator.system-default");
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (typeof root.model.get === 'function') {
|
||||
for (var i = 0; i < root.itemCount(); i++) {
|
||||
var item = root.getItem(i);
|
||||
if (item && item.key === "") {
|
||||
displayValue = item.name || I18n.tr("panels.indicator.system-default");
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If not found in model, show "System Default" instead of "(empty)"
|
||||
if (!found) {
|
||||
displayValue = I18n.tr("panels.indicator.system-default");
|
||||
}
|
||||
} else {
|
||||
// Try to find the display name for the default key in the model
|
||||
var found = false;
|
||||
if (root.model) {
|
||||
if (Array.isArray(root.model)) {
|
||||
for (var i = 0; i < root.model.length; i++) {
|
||||
var item = root.model[i];
|
||||
if (item && item.key === defaultValue) {
|
||||
displayValue = item.name || String(defaultValue);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (typeof root.model.get === 'function') {
|
||||
for (var i = 0; i < root.itemCount(); i++) {
|
||||
var item = root.getItem(i);
|
||||
if (item && item.key === defaultValue) {
|
||||
displayValue = item.name || String(defaultValue);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
displayValue = String(defaultValue);
|
||||
}
|
||||
}
|
||||
return I18n.tr("panels.indicator.default-value", {
|
||||
"value": displayValue
|
||||
});
|
||||
}
|
||||
|
||||
function itemCount() {
|
||||
if (!root.model)
|
||||
return 0;
|
||||
if (typeof root.model.count === 'number')
|
||||
return root.model.count;
|
||||
if (Array.isArray(root.model))
|
||||
return root.model.length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getItem(index) {
|
||||
if (!root.model)
|
||||
return null;
|
||||
if (typeof root.model.get === 'function')
|
||||
return root.model.get(index);
|
||||
if (Array.isArray(root.model))
|
||||
return root.model[index];
|
||||
return null;
|
||||
}
|
||||
|
||||
function findIndexByKey(key) {
|
||||
for (var i = 0; i < itemCount(); i++) {
|
||||
var item = getItem(i);
|
||||
if (item && item.key === key)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
NLabel {
|
||||
label: root.label
|
||||
description: root.description
|
||||
showIndicator: root.isValueChanged
|
||||
indicatorTooltip: root.indicatorTooltip
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: combo
|
||||
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
Layout.margins: Style.borderS
|
||||
Layout.minimumWidth: Math.round(root.minimumWidth * Style.uiScaleRatio)
|
||||
Layout.preferredHeight: Math.round(root.preferredHeight * Style.uiScaleRatio)
|
||||
implicitWidth: Layout.minimumWidth
|
||||
model: root.model
|
||||
textRole: "name"
|
||||
currentIndex: root.findIndexByKey(root.currentKey)
|
||||
|
||||
onActivated: {
|
||||
var item = root.getItem(combo.currentIndex);
|
||||
if (item && item.key !== undefined)
|
||||
root.selected(item.key);
|
||||
}
|
||||
|
||||
Keys.onUpPressed: event => {
|
||||
if (combo.popup.visible) {
|
||||
if (listView.currentIndex > 0) {
|
||||
listView.currentIndex--;
|
||||
listView.positionViewAtIndex(listView.currentIndex, ListView.Contain);
|
||||
}
|
||||
event.accepted = true;
|
||||
} else {
|
||||
event.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onDownPressed: event => {
|
||||
if (combo.popup.visible) {
|
||||
if (listView.currentIndex < root.itemCount() - 1) {
|
||||
listView.currentIndex++;
|
||||
listView.positionViewAtIndex(listView.currentIndex, ListView.Contain);
|
||||
}
|
||||
event.accepted = true;
|
||||
} else {
|
||||
event.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: event => {
|
||||
if (combo.popup.visible) {
|
||||
var item = root.getItem(listView.currentIndex);
|
||||
if (item && item.key !== undefined) {
|
||||
root.selected(item.key);
|
||||
combo.currentIndex = listView.currentIndex;
|
||||
combo.popup.close();
|
||||
}
|
||||
event.accepted = true;
|
||||
} else {
|
||||
event.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: event => {
|
||||
combo.Keys.returnPressed(event);
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: Math.round(Style.baseWidgetSize * 3.75 * Style.uiScaleRatio)
|
||||
implicitHeight: Math.round(root.preferredHeight * Style.uiScaleRatio)
|
||||
color: Color.mSurface
|
||||
border.color: combo.activeFocus ? Color.mSecondary : Color.mOutline
|
||||
border.width: Style.borderS
|
||||
radius: Style.iRadiusM
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onEntered: {
|
||||
if (root.tooltip != "") {
|
||||
TooltipService.show(root, root.tooltip);
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
if (root.tooltip != "") {
|
||||
TooltipService.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: NText {
|
||||
leftPadding: Style.marginL
|
||||
rightPadding: combo.indicator.width + Style.marginL
|
||||
pointSize: Style.fontSizeM
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
color: combo.currentIndex >= 0 ? Color.mOnSurface : Color.mOnSurfaceVariant
|
||||
text: {
|
||||
if (combo.currentIndex >= 0 && combo.currentIndex < root.itemCount()) {
|
||||
var item = root.getItem(combo.currentIndex);
|
||||
return item ? item.name : root.placeholder;
|
||||
}
|
||||
return root.placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
indicator: NIcon {
|
||||
x: combo.width - width - Style.marginM
|
||||
y: combo.topPadding + (combo.availableHeight - height) / 2
|
||||
icon: "caret-down"
|
||||
pointSize: Style.fontSizeL
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: combo.height + Style.marginS
|
||||
implicitWidth: combo.width
|
||||
implicitHeight: Math.min(Math.round(root.popupHeight * Style.uiScaleRatio), listView.contentHeight + Style.margin2M)
|
||||
padding: Style.marginM
|
||||
|
||||
onOpened: {
|
||||
listView.currentIndex = combo.currentIndex;
|
||||
listView.positionViewAtIndex(combo.currentIndex, ListView.Beginning);
|
||||
}
|
||||
|
||||
contentItem: NListView {
|
||||
id: listView
|
||||
property var comboBox: combo
|
||||
model: combo.popup.visible ? root.model : null
|
||||
highlightMoveDuration: 0
|
||||
//showGradientMasks: false
|
||||
|
||||
delegate: Rectangle {
|
||||
id: delegateRect
|
||||
required property int index
|
||||
property bool isHighlighted: listView.currentIndex === index
|
||||
|
||||
width: listView.availableWidth
|
||||
height: delegateText.implicitHeight + Style.margin2S
|
||||
radius: Style.iRadiusS
|
||||
color: isHighlighted ? Color.mHover : "transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
id: delegateText
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.marginM
|
||||
anchors.rightMargin: Style.marginM
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
pointSize: Style.fontSizeM
|
||||
color: delegateRect.isHighlighted ? Color.mOnHover : Color.mOnSurface
|
||||
text: {
|
||||
var item = root.getItem(delegateRect.index);
|
||||
return item && item.name ? item.name : "";
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse) {
|
||||
listView.currentIndex = delegateRect.index;
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
var item = root.getItem(delegateRect.index);
|
||||
if (item && item.key !== undefined) {
|
||||
root.selected(item.key);
|
||||
listView.comboBox.currentIndex = delegateRect.index;
|
||||
listView.comboBox.popup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Color.mSurfaceVariant
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
radius: Style.iRadiusM
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentKeyChanged() {
|
||||
combo.currentIndex = root.findIndexByKey(root.currentKey);
|
||||
}
|
||||
function onModelChanged() {
|
||||
combo.currentIndex = root.findIndexByKey(root.currentKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user