mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-08-15 18:43:59 +00:00
Merge branch 'main' into main
This commit is contained in:
@@ -69,7 +69,7 @@ Popup {
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: "Close"
|
||||
tooltipText: I18n.tr("tooltips.close")
|
||||
onClicked: widgetSettings.close()
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,8 @@ Popup {
|
||||
"SystemMonitor": "WidgetSettings/SystemMonitorSettings.qml",
|
||||
"Volume": "WidgetSettings/VolumeSettings.qml",
|
||||
"Workspace": "WidgetSettings/WorkspaceSettings.qml",
|
||||
"Taskbar": "WidgetSettings/TaskbarSettings.qml"
|
||||
"Taskbar": "WidgetSettings/TaskbarSettings.qml",
|
||||
"Tray": "WidgetSettings/TraySettings.qml"
|
||||
}
|
||||
|
||||
const source = widgetSettingsMap[widgetId]
|
||||
|
||||
@@ -21,7 +21,6 @@ ColumnLayout {
|
||||
var settings = Object.assign({}, widgetData || {})
|
||||
settings.onlySameOutput = valueOnlySameOutput
|
||||
settings.onlyActiveWorkspaces = valueOnlyActiveWorkspaces
|
||||
console.log(JSON.stringify(settings))
|
||||
return settings
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
// Properties to receive data from parent
|
||||
property var widgetData: ({}) // Expected by BarWidgetSettingsDialog
|
||||
property var widgetMetadata: ({}) // Expected by BarWidgetSettingsDialog
|
||||
|
||||
// Local state for the blacklist
|
||||
property var localBlacklist: widgetData.blacklist || []
|
||||
|
||||
ListModel {
|
||||
id: blacklistModel
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Populate the ListModel from localBlacklist
|
||||
for (var i = 0; i < localBlacklist.length; i++) {
|
||||
blacklistModel.append({
|
||||
"rule": localBlacklist[i]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NLabel {
|
||||
label: I18n.tr("settings.bar.tray.blacklist.label")
|
||||
description: I18n.tr("settings.bar.tray.blacklist.description")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NTextInput {
|
||||
id: newRuleInput
|
||||
Layout.fillWidth: true
|
||||
placeholderText: I18n.tr("settings.bar.tray.blacklist.placeholder")
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon: "add"
|
||||
baseSize: Style.baseWidgetSize * 0.8 * scaling
|
||||
onClicked: {
|
||||
if (newRuleInput.text.length > 0) {
|
||||
var newRule = newRuleInput.text.trim()
|
||||
var exists = false
|
||||
for (var i = 0; i < blacklistModel.count; i++) {
|
||||
if (blacklistModel.get(i).rule === newRule) {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
blacklistModel.append({
|
||||
"rule": newRule
|
||||
})
|
||||
newRuleInput.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
enabled: newRuleInput.text.length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List of current blacklist items
|
||||
ListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 150 * scaling
|
||||
Layout.topMargin: Style.marginL * scaling // Increased top margin
|
||||
clip: true
|
||||
model: blacklistModel
|
||||
delegate: Item {
|
||||
width: ListView.width
|
||||
height: 40 * scaling
|
||||
|
||||
Rectangle {
|
||||
id: itemBackground
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginXS * scaling
|
||||
color: Color.transparent // Make background transparent
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
radius: Style.radiusS * scaling
|
||||
visible: model.rule !== undefined && model.rule !== "" // Only visible if rule exists
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.marginS * scaling
|
||||
anchors.rightMargin: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NText {
|
||||
text: model.rule
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
width: 16 * scaling
|
||||
height: 16 * scaling
|
||||
icon: "close"
|
||||
baseSize: 8 * scaling
|
||||
colorBg: Color.mSurfaceVariant
|
||||
colorFg: Color.mOnSurface
|
||||
colorBgHover: Color.mError
|
||||
colorFgHover: Color.mOnError
|
||||
onClicked: {
|
||||
blacklistModel.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function will be called by the dialog to get the new settings
|
||||
function saveSettings() {
|
||||
var newBlacklist = []
|
||||
for (var i = 0; i < blacklistModel.count; i++) {
|
||||
newBlacklist.push(blacklistModel.get(i).rule)
|
||||
}
|
||||
|
||||
// Return the updated settings for this widget instance
|
||||
var settings = Object.assign({}, widgetData || {})
|
||||
settings.blacklist = newBlacklist
|
||||
return settings
|
||||
}
|
||||
}
|
||||
+12
-9
@@ -13,6 +13,10 @@ NBox {
|
||||
property string sectionId: ""
|
||||
property var widgetModel: []
|
||||
property var availableWidgets: []
|
||||
property bool enableMoveBetweenSections: true
|
||||
|
||||
property var widgetRegistry: null
|
||||
property string settingsDialogComponent: "BarWidgetSettingsDialog.qml"
|
||||
|
||||
readonly property real miniButtonSize: Style.baseWidgetSize * 0.65
|
||||
|
||||
@@ -154,7 +158,7 @@ NBox {
|
||||
// Store the widget index for drag operations
|
||||
property int widgetIndex: index
|
||||
readonly property int buttonsWidth: Math.round(20 * scaling)
|
||||
readonly property int buttonsCount: 1 + BarWidgetRegistry.widgetHasUserSettings(modelData.id)
|
||||
readonly property int buttonsCount: 1 + (root.widgetRegistry ? root.widgetRegistry.widgetHasUserSettings(modelData.id) : 0)
|
||||
|
||||
// Visual feedback during drag
|
||||
opacity: flowDragArea.draggedIndex === index ? 0.5 : 1.0
|
||||
@@ -197,9 +201,10 @@ NBox {
|
||||
onTriggered: action => root.moveWidget(root.sectionId, index, action)
|
||||
}
|
||||
|
||||
// Update the MouseArea to use the new context menu
|
||||
// MouseArea for the context menu
|
||||
MouseArea {
|
||||
id: contextMouseArea
|
||||
enabled: enableMoveBetweenSections
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
z: -1 // Below the buttons but above background
|
||||
@@ -209,9 +214,7 @@ NBox {
|
||||
// Check if click is not on the buttons area
|
||||
const localX = mouse.x
|
||||
const buttonsStartX = parent.width - (parent.buttonsCount * parent.buttonsWidth)
|
||||
|
||||
if (localX < buttonsStartX) {
|
||||
// Use the helper function to open at mouse position
|
||||
contextMenu.openAtItem(widgetItem, mouse.x, mouse.y)
|
||||
}
|
||||
}
|
||||
@@ -236,7 +239,7 @@ NBox {
|
||||
Layout.preferredWidth: buttonsCount * buttonsWidth
|
||||
|
||||
Loader {
|
||||
active: BarWidgetRegistry.widgetHasUserSettings(modelData.id)
|
||||
active: root.widgetRegistry && root.widgetRegistry.widgetHasUserSettings(modelData.id)
|
||||
sourceComponent: NIconButton {
|
||||
icon: "settings"
|
||||
tooltipText: I18n.tr("tooltips.widget-settings")
|
||||
@@ -247,7 +250,7 @@ NBox {
|
||||
colorBgHover: Qt.alpha(Color.mOnPrimary, Style.opacityLight)
|
||||
colorFgHover: Color.mOnPrimary
|
||||
onClicked: {
|
||||
var component = Qt.createComponent(Qt.resolvedUrl("BarWidgetSettingsDialog.qml"))
|
||||
var component = Qt.createComponent(Qt.resolvedUrl(root.settingsDialogComponent))
|
||||
function instantiateAndOpen() {
|
||||
var dialog = component.createObject(root, {
|
||||
"widgetIndex": index,
|
||||
@@ -258,19 +261,19 @@ NBox {
|
||||
if (dialog) {
|
||||
dialog.open()
|
||||
} else {
|
||||
Logger.error("BarSectionEditor", "Failed to create settings dialog instance")
|
||||
Logger.error("WidgetSectionEditor", "Failed to create settings dialog instance")
|
||||
}
|
||||
}
|
||||
if (component.status === Component.Ready) {
|
||||
instantiateAndOpen()
|
||||
} else if (component.status === Component.Error) {
|
||||
Logger.error("BarSectionEditor", component.errorString())
|
||||
Logger.error("WidgetSectionEditor", component.errorString())
|
||||
} else {
|
||||
component.statusChanged.connect(function () {
|
||||
if (component.status === Component.Ready) {
|
||||
instantiateAndOpen()
|
||||
} else if (component.status === Component.Error) {
|
||||
Logger.error("BarSectionEditor", component.errorString())
|
||||
Logger.error("WidgetSectionEditor", component.errorString())
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -29,6 +29,7 @@ NPanel {
|
||||
Audio,
|
||||
Bar,
|
||||
ColorScheme,
|
||||
ControlCenter,
|
||||
OSD,
|
||||
Display,
|
||||
Dock,
|
||||
@@ -111,6 +112,10 @@ NPanel {
|
||||
id: notificationsTab
|
||||
NotificationsTab {}
|
||||
}
|
||||
Component {
|
||||
id: controlCenterTab
|
||||
ControlCenterTab {}
|
||||
}
|
||||
|
||||
// Order *DOES* matter
|
||||
function updateTabsModel() {
|
||||
@@ -124,6 +129,11 @@ NPanel {
|
||||
"label": "settings.bar.title",
|
||||
"icon": "settings-bar",
|
||||
"source": barTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.ControlCenter,
|
||||
"label": "settings.control-center.title",
|
||||
"icon": "settings-bar",
|
||||
"source": controlCenterTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Dock,
|
||||
"label": "settings.dock.title",
|
||||
|
||||
@@ -5,7 +5,7 @@ import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Bar
|
||||
import qs.Modules.Settings.Extras
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
@@ -201,9 +201,11 @@ ColumnLayout {
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Left Section
|
||||
BarSectionEditor {
|
||||
SectionEditor {
|
||||
sectionName: "Left"
|
||||
sectionId: "left"
|
||||
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/Bar/BarWidgetSettingsDialog.qml")
|
||||
widgetRegistry: BarWidgetRegistry
|
||||
widgetModel: Settings.data.bar.widgets.left
|
||||
availableWidgets: availableWidgets
|
||||
onAddWidget: (widgetId, section) => _addWidgetToSection(widgetId, section)
|
||||
@@ -216,9 +218,11 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
// Center Section
|
||||
BarSectionEditor {
|
||||
SectionEditor {
|
||||
sectionName: "Center"
|
||||
sectionId: "center"
|
||||
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/Bar/BarWidgetSettingsDialog.qml")
|
||||
widgetRegistry: BarWidgetRegistry
|
||||
widgetModel: Settings.data.bar.widgets.center
|
||||
availableWidgets: availableWidgets
|
||||
onAddWidget: (widgetId, section) => _addWidgetToSection(widgetId, section)
|
||||
@@ -231,9 +235,11 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
// Right Section
|
||||
BarSectionEditor {
|
||||
SectionEditor {
|
||||
sectionName: "Right"
|
||||
sectionId: "right"
|
||||
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/Bar/BarWidgetSettingsDialog.qml")
|
||||
widgetRegistry: BarWidgetRegistry
|
||||
widgetModel: Settings.data.bar.widgets.right
|
||||
availableWidgets: availableWidgets
|
||||
onAddWidget: (widgetId, section) => _addWidgetToSection(widgetId, section)
|
||||
|
||||
@@ -506,22 +506,29 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Vesktop"
|
||||
description: ProgramCheckerService.vesktopAvailable ? I18n.tr("settings.color-scheme.templates.programs.vesktop.description", {
|
||||
"filepath": "~/.config/vesktop/themes/noctalia.theme.css"
|
||||
}) : I18n.tr("settings.color-scheme.templates.programs.vesktop.description-missing", {
|
||||
"app": "vesktop"
|
||||
})
|
||||
checked: Settings.data.templates.vesktop
|
||||
enabled: ProgramCheckerService.vesktopAvailable
|
||||
opacity: ProgramCheckerService.vesktopAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.vesktopAvailable) {
|
||||
Settings.data.templates.vesktop = checked
|
||||
// Show individual checkboxes for each detected Discord client
|
||||
Repeater {
|
||||
model: ProgramCheckerService.availableDiscordClients
|
||||
delegate: NCheckbox {
|
||||
label: modelData.name.charAt(0).toUpperCase() + modelData.name.slice(1)
|
||||
description: I18n.tr("settings.color-scheme.templates.programs.discord.description", {
|
||||
"client": modelData.name.charAt(0).toUpperCase() + modelData.name.slice(1),
|
||||
"filepath": modelData.themePath
|
||||
})
|
||||
checked: Settings.data.templates["discord_" + modelData.name] || false
|
||||
onToggled: checked => {
|
||||
Settings.data.templates["discord_" + modelData.name] = checked
|
||||
AppThemeService.generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show message if no Discord clients detected
|
||||
NText {
|
||||
visible: ProgramCheckerService.availableDiscordClients.length === 0
|
||||
text: I18n.tr("settings.color-scheme.templates.programs.discord.description-missing")
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeS * scaling
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Extras
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: Style.marginL * scaling
|
||||
|
||||
// Handler for drag start - disables panel background clicks
|
||||
function handleDragStart() {
|
||||
var panel = PanelService.getPanel("settingsPanel")
|
||||
if (panel && panel.disableBackgroundClick) {
|
||||
panel.disableBackgroundClick()
|
||||
}
|
||||
}
|
||||
|
||||
// Handler for drag end - re-enables panel background clicks
|
||||
function handleDragEnd() {
|
||||
var panel = PanelService.getPanel("settingsPanel")
|
||||
if (panel && panel.enableBackgroundClick) {
|
||||
panel.enableBackgroundClick()
|
||||
}
|
||||
}
|
||||
|
||||
// Quick Settings Style Section
|
||||
ColumnLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NHeader {
|
||||
label: I18n.tr("settings.control-center.quickSettingsStyle.section.label")
|
||||
description: I18n.tr("settings.control-center.quickSettingsStyle.section.description")
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
id: quickSettingsStyle
|
||||
label: I18n.tr("settings.control-center.quickSettingsStyle.style.label")
|
||||
description: I18n.tr("settings.control-center.quickSettingsStyle.style.description")
|
||||
Layout.fillWidth: true
|
||||
model: [{
|
||||
"key": "compact",
|
||||
"name": I18n.tr("options.control-center.quickSettingsStyle.compact")
|
||||
}, {
|
||||
"key": "classic",
|
||||
"name": I18n.tr("options.control-center.quickSettingsStyle.classic")
|
||||
}, {
|
||||
"key": "modern",
|
||||
"name": I18n.tr("options.control-center.quickSettingsStyle.modern")
|
||||
}]
|
||||
currentKey: Settings.data.controlCenter.quickSettingsStyle || "compact"
|
||||
onSelected: function (key) {
|
||||
Settings.data.controlCenter.quickSettingsStyle = key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// Widgets Management Section
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXXS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NHeader {
|
||||
label: I18n.tr("settings.control-center.widgets.section.label")
|
||||
description: I18n.tr("settings.control-center.widgets.section.description")
|
||||
}
|
||||
|
||||
// Bar Sections
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: Style.marginM * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Quick Settings
|
||||
SectionEditor {
|
||||
sectionName: I18n.tr("settings.control-center.quickSettings.sectionName")
|
||||
sectionId: "quickSettings"
|
||||
settingsDialogComponent: ""
|
||||
widgetRegistry: ControlCenterWidgetRegistry
|
||||
widgetModel: Settings.data.controlCenter.widgets["quickSettings"]
|
||||
availableWidgets: availableWidgets
|
||||
enableMoveBetweenSections: false
|
||||
onAddWidget: (widgetId, section) => _addWidgetToSection(widgetId, section)
|
||||
onRemoveWidget: (section, index) => _removeWidgetFromSection(section, index)
|
||||
onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex)
|
||||
onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings)
|
||||
onDragPotentialStarted: root.handleDragStart()
|
||||
onDragPotentialEnded: root.handleDragEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// Signal functions
|
||||
// ---------------------------------
|
||||
function _addWidgetToSection(widgetId, section) {
|
||||
var newWidget = {
|
||||
"id": widgetId
|
||||
}
|
||||
if (ControlCenterWidgetRegistry.widgetHasUserSettings(widgetId)) {
|
||||
var metadata = ControlCenterWidgetRegistry.widgetMetadata[widgetId]
|
||||
if (metadata) {
|
||||
Object.keys(metadata).forEach(function (key) {
|
||||
if (key !== "allowUserSettings") {
|
||||
newWidget[key] = metadata[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Settings.data.controlCenter.widgets[section].push(newWidget)
|
||||
}
|
||||
|
||||
function _removeWidgetFromSection(section, index) {
|
||||
if (index >= 0 && index < Settings.data.controlCenter.widgets[section].length) {
|
||||
var newArray = Settings.data.controlCenter.widgets[section].slice()
|
||||
var removedWidgets = newArray.splice(index, 1)
|
||||
Settings.data.controlCenter.widgets[section] = newArray
|
||||
|
||||
// Check that we still have a control center
|
||||
if (removedWidgets[0].id === "ControlCenter" && BarService.lookupWidget("ControlCenter") === undefined) {
|
||||
ToastService.showWarning(I18n.tr("toast.missing-control-center.label"), I18n.tr("toast.missing-control-center.description"), 12000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _reorderWidgetInSection(section, fromIndex, toIndex) {
|
||||
if (fromIndex >= 0 && fromIndex < Settings.data.controlCenter.widgets[section].length && toIndex >= 0 && toIndex < Settings.data.controlCenter.widgets[section].length) {
|
||||
|
||||
// Create a new array to avoid modifying the original
|
||||
var newArray = Settings.data.controlCenter.widgets[section].slice()
|
||||
var item = newArray[fromIndex]
|
||||
newArray.splice(fromIndex, 1)
|
||||
newArray.splice(toIndex, 0, item)
|
||||
|
||||
Settings.data.controlCenter.widgets[section] = newArray
|
||||
//Logger.log("BarTab", "Widget reordered. New array:", JSON.stringify(newArray))
|
||||
}
|
||||
}
|
||||
|
||||
function _updateWidgetSettingsInSection(section, index, settings) {
|
||||
// Update the widget settings in the Settings data
|
||||
Settings.data.controlCenter.widgets[section][index] = settings
|
||||
//Logger.log("BarTab", `Updated widget settings for ${settings.id} in ${section} section`)
|
||||
}
|
||||
|
||||
// Base list model for all combo boxes
|
||||
ListModel {
|
||||
id: availableWidgets
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Fill out availableWidgets ListModel
|
||||
availableWidgets.clear()
|
||||
ControlCenterWidgetRegistry.getAvailableWidgets().forEach(entry => {
|
||||
availableWidgets.append({
|
||||
"key": entry,
|
||||
"name": entry
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user