From 8e562e070120ed64710da9ef95048c01d5c355ca Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 16:37:45 +0200 Subject: [PATCH 01/10] WIP - modular bar (need to fix brightness & tray) --- Commons/Settings.qml | 9 + Modules/Bar/Bar.qml | 115 +-- Modules/Bar/{ => Widgets}/ActiveWindow.qml | 0 Modules/Bar/{ => Widgets}/Battery.qml | 0 Modules/Bar/{ => Widgets}/Bluetooth.qml | 2 - Modules/Bar/{ => Widgets}/BluetoothPanel.qml | 2 +- Modules/Bar/{ => Widgets}/Brightness.qml | 2 +- Modules/Bar/{ => Widgets}/Clock.qml | 0 Modules/Bar/{ => Widgets}/MediaMini.qml | 0 .../Bar/{ => Widgets}/NotificationHistory.qml | 0 .../{ => Widgets}/ScreenRecorderIndicator.qml | 0 Modules/Bar/{ => Widgets}/SidePanelToggle.qml | 0 Modules/Bar/{ => Widgets}/SystemMonitor.qml | 0 Modules/Bar/{ => Widgets}/Tray.qml | 0 Modules/Bar/{ => Widgets}/TrayMenu.qml | 2 +- Modules/Bar/{ => Widgets}/Volume.qml | 0 Modules/Bar/{ => Widgets}/WiFi.qml | 3 - Modules/Bar/{ => Widgets}/WiFiPanel.qml | 2 +- Modules/Bar/{ => Widgets}/Workspace.qml | 0 Modules/SettingsPanel/SettingsPanel.qml | 5 +- Modules/SettingsPanel/Tabs/AboutTab.qml | 4 +- Modules/SettingsPanel/Tabs/BarTab.qml | 676 ++++++++++++++++-- Modules/SettingsPanel/Tabs/BrightnessTab.qml | 31 +- Modules/SettingsPanel/Tabs/ColorSchemeTab.qml | 24 +- Modules/SettingsPanel/Tabs/GeneralTab.qml | 7 + Modules/SettingsPanel/Tabs/LauncherTab.qml | 4 +- Modules/SettingsPanel/Tabs/NetworkTab.qml | 7 + .../SettingsPanel/Tabs/ScreenRecorderTab.qml | 8 + Modules/SettingsPanel/Tabs/TimeWeatherTab.qml | 8 + .../Tabs/WallpaperSelectorTab.qml | 2 +- Modules/SettingsPanel/Tabs/WallpaperTab.qml | 8 + 31 files changed, 785 insertions(+), 136 deletions(-) rename Modules/Bar/{ => Widgets}/ActiveWindow.qml (100%) rename Modules/Bar/{ => Widgets}/Battery.qml (100%) rename Modules/Bar/{ => Widgets}/Bluetooth.qml (87%) rename Modules/Bar/{ => Widgets}/BluetoothPanel.qml (99%) rename Modules/Bar/{ => Widgets}/Brightness.qml (96%) rename Modules/Bar/{ => Widgets}/Clock.qml (100%) rename Modules/Bar/{ => Widgets}/MediaMini.qml (100%) rename Modules/Bar/{ => Widgets}/NotificationHistory.qml (100%) rename Modules/Bar/{ => Widgets}/ScreenRecorderIndicator.qml (100%) rename Modules/Bar/{ => Widgets}/SidePanelToggle.qml (100%) rename Modules/Bar/{ => Widgets}/SystemMonitor.qml (100%) rename Modules/Bar/{ => Widgets}/Tray.qml (100%) rename Modules/Bar/{ => Widgets}/TrayMenu.qml (99%) rename Modules/Bar/{ => Widgets}/Volume.qml (100%) rename Modules/Bar/{ => Widgets}/WiFi.qml (89%) rename Modules/Bar/{ => Widgets}/WiFiPanel.qml (99%) rename Modules/Bar/{ => Widgets}/Workspace.qml (100%) diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 23fa432..0ffe01c 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -130,6 +130,15 @@ Singleton { property bool alwaysShowBatteryPercentage: false property real backgroundOpacity: 1.0 property list monitors: [] + + // Widget configuration for modular bar system + property JsonObject widgets + + widgets: JsonObject { + property list left: ["SystemMonitor", "ActiveWindow", "MediaMini"] + property list center: ["Workspace"] + property list right: ["ScreenRecorderIndicator", "Tray", "NotificationHistory", "WiFi", "Bluetooth", "Battery", "Volume", "Brightness", "Clock", "SidePanelToggle"] + } } // general diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 6cc30a7..61e7940 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -47,7 +47,7 @@ Variants { layer.enabled: true } - // Left + // Left Section - Dynamic Widgets Row { id: leftSection @@ -57,14 +57,25 @@ Variants { anchors.verticalCenter: parent.verticalCenter spacing: Style.marginS * scaling - SystemMonitor {} - - ActiveWindow {} - - MediaMini {} + Repeater { + model: Settings.data.bar.widgets.left + delegate: Loader { + id: widgetLoader + sourceComponent: getWidgetComponent(modelData) + active: true + anchors.verticalCenter: parent.verticalCenter + onStatusChanged: { + if (status === Loader.Error) { + console.warn(`Failed to load widget: ${modelData}`) + } else if (status === Loader.Ready) { + console.log(`Successfully loaded widget: ${modelData}`) + } + } + } + } } - // Center + // Center Section - Dynamic Widgets Row { id: centerSection @@ -73,10 +84,25 @@ Variants { anchors.verticalCenter: parent.verticalCenter spacing: Style.marginS * scaling - Workspace {} + Repeater { + model: Settings.data.bar.widgets.center + delegate: Loader { + id: widgetLoader + sourceComponent: getWidgetComponent(modelData) + active: true + anchors.verticalCenter: parent.verticalCenter + onStatusChanged: { + if (status === Loader.Error) { + console.warn(`Failed to load widget: ${modelData}`) + } else if (status === Loader.Ready) { + console.log(`Successfully loaded widget: ${modelData}`) + } + } + } + } } - // Right + // Right Section - Dynamic Widgets Row { id: rightSection @@ -86,44 +112,43 @@ Variants { anchors.verticalCenter: bar.verticalCenter spacing: Style.marginS * scaling - ScreenRecorderIndicator { - anchors.verticalCenter: parent.verticalCenter + Repeater { + model: Settings.data.bar.widgets.right + delegate: Loader { + id: widgetLoader + sourceComponent: getWidgetComponent(modelData) + active: true + anchors.verticalCenter: parent.verticalCenter + onStatusChanged: { + if (status === Loader.Error) { + console.warn(`Failed to load widget: ${modelData}`) + } else if (status === Loader.Ready) { + console.log(`Successfully loaded widget: ${modelData}`) + } + } + } } - - Tray { - anchors.verticalCenter: parent.verticalCenter - } - - NotificationHistory { - anchors.verticalCenter: parent.verticalCenter - } - - WiFi { - anchors.verticalCenter: parent.verticalCenter - } - - Bluetooth { - anchors.verticalCenter: parent.verticalCenter - } - - Battery { - anchors.verticalCenter: parent.verticalCenter - } - - Volume { - anchors.verticalCenter: parent.verticalCenter - } - - Brightness { - anchors.verticalCenter: parent.verticalCenter - } - - Clock { - anchors.verticalCenter: parent.verticalCenter - } - - SidePanelToggle {} } } + + // Auto-discover widget components + function getWidgetComponent(widgetName) { + if (!widgetName || widgetName.trim() === "") { + return null + } + + // Try to load the widget directly from file + const component = Qt.createComponent(`../Bar/Widgets/${widgetName}.qml`) + if (component.status === Component.Ready) { + return component + } + + console.warn(`Failed to load widget: ${widgetName}.qml`) + return null + } + + + + } } diff --git a/Modules/Bar/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml similarity index 100% rename from Modules/Bar/ActiveWindow.qml rename to Modules/Bar/Widgets/ActiveWindow.qml diff --git a/Modules/Bar/Battery.qml b/Modules/Bar/Widgets/Battery.qml similarity index 100% rename from Modules/Bar/Battery.qml rename to Modules/Bar/Widgets/Battery.qml diff --git a/Modules/Bar/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml similarity index 87% rename from Modules/Bar/Bluetooth.qml rename to Modules/Bar/Widgets/Bluetooth.qml index adc76e0..09c5c60 100644 --- a/Modules/Bar/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -10,9 +10,7 @@ import qs.Widgets NIconButton { id: root - readonly property bool bluetoothEnabled: Settings.data.network.bluetoothEnabled sizeMultiplier: 0.8 - visible: bluetoothEnabled colorBg: Color.mSurfaceVariant colorFg: Color.mOnSurface diff --git a/Modules/Bar/BluetoothPanel.qml b/Modules/Bar/Widgets/BluetoothPanel.qml similarity index 99% rename from Modules/Bar/BluetoothPanel.qml rename to Modules/Bar/Widgets/BluetoothPanel.qml index b583897..eeb4c7f 100644 --- a/Modules/Bar/BluetoothPanel.qml +++ b/Modules/Bar/Widgets/BluetoothPanel.qml @@ -118,7 +118,7 @@ NPanel { radius: Style.radiusM * scaling color: { if (availableDeviceArea.containsMouse && !isBusy) - return Color.mSecondary + return Color.mTertiary if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting) return Color.mPrimary diff --git a/Modules/Bar/Brightness.qml b/Modules/Bar/Widgets/Brightness.qml similarity index 96% rename from Modules/Bar/Brightness.qml rename to Modules/Bar/Widgets/Brightness.qml index 39c0fb8..82bacb8 100644 --- a/Modules/Bar/Brightness.qml +++ b/Modules/Bar/Widgets/Brightness.qml @@ -10,7 +10,7 @@ Item { width: pill.width height: pill.height - visible: Settings.data.bar.showBrightness && firstBrightnessReceived && getMonitor() !== null + visible: firstBrightnessReceived && getMonitor() !== null // Used to avoid opening the pill on Quickshell startup property bool firstBrightnessReceived: false diff --git a/Modules/Bar/Clock.qml b/Modules/Bar/Widgets/Clock.qml similarity index 100% rename from Modules/Bar/Clock.qml rename to Modules/Bar/Widgets/Clock.qml diff --git a/Modules/Bar/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml similarity index 100% rename from Modules/Bar/MediaMini.qml rename to Modules/Bar/Widgets/MediaMini.qml diff --git a/Modules/Bar/NotificationHistory.qml b/Modules/Bar/Widgets/NotificationHistory.qml similarity index 100% rename from Modules/Bar/NotificationHistory.qml rename to Modules/Bar/Widgets/NotificationHistory.qml diff --git a/Modules/Bar/ScreenRecorderIndicator.qml b/Modules/Bar/Widgets/ScreenRecorderIndicator.qml similarity index 100% rename from Modules/Bar/ScreenRecorderIndicator.qml rename to Modules/Bar/Widgets/ScreenRecorderIndicator.qml diff --git a/Modules/Bar/SidePanelToggle.qml b/Modules/Bar/Widgets/SidePanelToggle.qml similarity index 100% rename from Modules/Bar/SidePanelToggle.qml rename to Modules/Bar/Widgets/SidePanelToggle.qml diff --git a/Modules/Bar/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml similarity index 100% rename from Modules/Bar/SystemMonitor.qml rename to Modules/Bar/Widgets/SystemMonitor.qml diff --git a/Modules/Bar/Tray.qml b/Modules/Bar/Widgets/Tray.qml similarity index 100% rename from Modules/Bar/Tray.qml rename to Modules/Bar/Widgets/Tray.qml diff --git a/Modules/Bar/TrayMenu.qml b/Modules/Bar/Widgets/TrayMenu.qml similarity index 99% rename from Modules/Bar/TrayMenu.qml rename to Modules/Bar/Widgets/TrayMenu.qml index 699d8d1..a3a24fd 100644 --- a/Modules/Bar/TrayMenu.qml +++ b/Modules/Bar/Widgets/TrayMenu.qml @@ -134,7 +134,7 @@ PopupWindow { Rectangle { anchors.fill: parent - color: mouseArea.containsMouse ? Color.mSecondary : Color.transparent + color: mouseArea.containsMouse ? Color.mTertiary : Color.transparent radius: Style.radiusS * scaling visible: !(modelData?.isSeparator ?? false) diff --git a/Modules/Bar/Volume.qml b/Modules/Bar/Widgets/Volume.qml similarity index 100% rename from Modules/Bar/Volume.qml rename to Modules/Bar/Widgets/Volume.qml diff --git a/Modules/Bar/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml similarity index 89% rename from Modules/Bar/WiFi.qml rename to Modules/Bar/Widgets/WiFi.qml index 6b9b921..cdae9be 100644 --- a/Modules/Bar/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -10,10 +10,7 @@ import qs.Widgets NIconButton { id: root - readonly property bool wifiEnabled: Settings.data.network.wifiEnabled - sizeMultiplier: 0.8 - visible: wifiEnabled colorBg: Color.mSurfaceVariant colorFg: Color.mOnSurface diff --git a/Modules/Bar/WiFiPanel.qml b/Modules/Bar/Widgets/WiFiPanel.qml similarity index 99% rename from Modules/Bar/WiFiPanel.qml rename to Modules/Bar/Widgets/WiFiPanel.qml index ce5acef..8d88cd9 100644 --- a/Modules/Bar/WiFiPanel.qml +++ b/Modules/Bar/Widgets/WiFiPanel.qml @@ -147,7 +147,7 @@ NPanel { Layout.fillWidth: true Layout.preferredHeight: Style.baseWidgetSize * 1.5 * scaling radius: Style.radiusS * scaling - color: modelData.connected ? Color.mPrimary : (networkMouseArea.containsMouse ? Color.mSecondary : Color.transparent) + color: modelData.connected ? Color.mPrimary : (networkMouseArea.containsMouse ? Color.mTertiary : Color.transparent) RowLayout { anchors.fill: parent diff --git a/Modules/Bar/Workspace.qml b/Modules/Bar/Widgets/Workspace.qml similarity index 100% rename from Modules/Bar/Workspace.qml rename to Modules/Bar/Widgets/Workspace.qml diff --git a/Modules/SettingsPanel/SettingsPanel.qml b/Modules/SettingsPanel/SettingsPanel.qml index 0224d89..b5667f9 100644 --- a/Modules/SettingsPanel/SettingsPanel.qml +++ b/Modules/SettingsPanel/SettingsPanel.qml @@ -47,6 +47,7 @@ NPanel { id: barTab Tabs.BarTab {} } + Component { id: audioTab Tabs.AudioTab {} @@ -202,7 +203,7 @@ NPanel { width: parent.width height: 32 * scaling radius: Style.radiusS * scaling - color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mSecondary : Color.transparent) + color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mTertiary : Color.transparent) readonly property bool selected: index === currentTabIndex property bool hovering: false property color tabTextColor: selected ? Color.mOnPrimary : (tabItem.hovering ? Color.mOnTertiary : Color.mOnSurface) @@ -265,7 +266,7 @@ NPanel { // Tab label on the main right side NText { text: root.tabsModel[currentTabIndex].label - font.pointSize: Style.fontSizeXL * scaling + font.pointSize: Style.fontSizeL * scaling font.weight: Style.fontWeightBold color: Color.mPrimary Layout.fillWidth: true diff --git a/Modules/SettingsPanel/Tabs/AboutTab.qml b/Modules/SettingsPanel/Tabs/AboutTab.qml index a7b7b3d..405ed1b 100644 --- a/Modules/SettingsPanel/Tabs/AboutTab.qml +++ b/Modules/SettingsPanel/Tabs/AboutTab.qml @@ -199,7 +199,7 @@ ColumnLayout { width: contributorsGrid.cellWidth - Style.marginL * scaling height: contributorsGrid.cellHeight - Style.marginXS * scaling radius: Style.radiusL * scaling - color: contributorArea.containsMouse ? Color.mSecondary : Color.transparent + color: contributorArea.containsMouse ? Color.mTertiary : Color.transparent RowLayout { anchors.fill: parent @@ -217,7 +217,7 @@ ColumnLayout { anchors.margins: Style.marginXS * scaling fallbackIcon: "person" borderColor: Color.mPrimary - borderWidth: Math.max(1, Style.borderL * scaling) + borderWidth: Math.max(1, Style.borderM * scaling) imageRadius: width * 0.5 } } diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index fe5cb42..d03c333 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -33,6 +33,13 @@ ColumnLayout { spacing: Style.marginL * scaling Layout.fillWidth: true + NText { + text: "Bar & Widgets" + font.pointSize: Style.fontSizeXXL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + ColumnLayout { spacing: Style.marginXXS * scaling Layout.fillWidth: true @@ -71,15 +78,6 @@ ColumnLayout { } } - NToggle { - label: "Show Active Window" - description: "Display the title of the currently focused window." - checked: Settings.data.bar.showActiveWindow - onToggled: checked => { - Settings.data.bar.showActiveWindow = checked - } - } - NToggle { label: "Show Active Window's Icon" description: "Display the app icon next to the title of the currently focused window." @@ -89,42 +87,6 @@ ColumnLayout { } } - NToggle { - label: "Show System Info" - description: "Display system statistics (CPU, RAM, Temperature)." - checked: Settings.data.bar.showSystemInfo - onToggled: checked => { - Settings.data.bar.showSystemInfo = checked - } - } - - NToggle { - label: "Show Media" - description: "Display media controls and information." - checked: Settings.data.bar.showMedia - onToggled: checked => { - Settings.data.bar.showMedia = checked - } - } - - NToggle { - label: "Show Notifications History" - description: "Display a shortcut to the notifications history." - checked: Settings.data.bar.showNotificationsHistory - onToggled: checked => { - Settings.data.bar.showNotificationsHistory = checked - } - } - - NToggle { - label: "Show Applications Tray" - description: "Display the applications tray." - checked: Settings.data.bar.showTray - onToggled: checked => { - Settings.data.bar.showTray = checked - } - } - NToggle { label: "Show Battery Percentage" description: "Show battery percentage at all times." @@ -172,7 +134,631 @@ ColumnLayout { } } } + + // Widget Management Section + ColumnLayout { + spacing: Style.marginXXS * scaling + Layout.fillWidth: true + + NText { + text: "Widget Management" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + + NText { + text: "Configure which widgets appear in each section of the bar. Use the arrow buttons to reorder widgets, or the add/remove buttons to manage them." + font.pointSize: Style.fontSizeXS * scaling + color: Color.mOnSurfaceVariant + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + // Bar Sections + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: Style.marginM * scaling + + // Left Section + NCard { + Layout.fillWidth: true + Layout.minimumHeight: { + var widgetCount = Settings.data.bar.widgets.left.length + if (widgetCount === 0) return 120 * scaling + + var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins + var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing + var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) + var rows = Math.ceil(widgetCount / widgetsPerRow) + + // Header (40) + spacing (16) + (rows * widget height) + (rows-1 * spacing) + bottom margin (16) + return (40 + 16 + (rows * 48) + ((rows - 1) * Style.marginS) + 16) * scaling + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Style.marginM * scaling + spacing: Style.marginM * scaling + + RowLayout { + Layout.fillWidth: true + + NText { + text: "Left Section" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + + Item { Layout.fillWidth: true } + + Rectangle { + width: 32 * scaling + height: 32 * scaling + radius: width * 0.5 + color: Color.mPrimary + border.color: Color.mPrimaryContainer + border.width: 2 * scaling + + NIcon { + anchors.centerIn: parent + text: "add" + color: Color.mOnPrimary + font.pointSize: Style.fontSizeM * scaling + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + addWidgetDialog.show("left") + } + } + } + } + + Flow { + id: leftWidgetsFlow + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: 52 * scaling + spacing: Style.marginS * scaling + flow: Flow.LeftToRight + + Repeater { + model: Settings.data.bar.widgets.left + delegate: Rectangle { + width: widgetContent.implicitWidth + 16 * scaling + height: 48 * scaling + radius: Style.radiusS * scaling + color: Color.mPrimary + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + + RowLayout { + id: widgetContent + anchors.centerIn: parent + spacing: Style.marginXS * scaling + + NIconButton { + icon: "chevron_left" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + enabled: index > 0 + onClicked: { + if (index > 0) { + reorderWidgetInSection("left", index, index - 1) + } + } + } + + NText { + text: modelData + font.pointSize: Style.fontSizeS * scaling + color: Color.mOnPrimary + horizontalAlignment: Text.AlignHCenter + } + + NIconButton { + icon: "chevron_right" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + enabled: index < Settings.data.bar.widgets.left.length - 1 + onClicked: { + if (index < Settings.data.bar.widgets.left.length - 1) { + reorderWidgetInSection("left", index, index + 1) + } + } + } + + NIconButton { + icon: "close" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + onClicked: { + removeWidgetFromSection("left", index) + } + } + } + } + } + } + } + } + + // Center Section + NCard { + Layout.fillWidth: true + Layout.minimumHeight: { + var widgetCount = Settings.data.bar.widgets.center.length + if (widgetCount === 0) return 120 * scaling + + var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins + var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing + var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) + var rows = Math.ceil(widgetCount / widgetsPerRow) + + // Header (40) + spacing (16) + (rows * widget height) + (rows-1 * spacing) + bottom margin (16) + return (40 + 16 + (rows * 48) + ((rows - 1) * Style.marginS) + 16) * scaling + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Style.marginM * scaling + spacing: Style.marginM * scaling + + RowLayout { + Layout.fillWidth: true + + NText { + text: "Center Section" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + + Item { Layout.fillWidth: true } + + Rectangle { + width: 32 * scaling + height: 32 * scaling + radius: width * 0.5 + color: Color.mPrimary + border.color: Color.mPrimaryContainer + border.width: 2 * scaling + + NIcon { + anchors.centerIn: parent + text: "add" + color: Color.mOnPrimary + font.pointSize: Style.fontSizeM * scaling + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + addWidgetDialog.show("center") + } + } + } + } + + Flow { + id: centerWidgetsFlow + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: 52 * scaling + spacing: Style.marginS * scaling + flow: Flow.LeftToRight + + Repeater { + model: Settings.data.bar.widgets.center + delegate: Rectangle { + width: widgetContent.implicitWidth + 16 * scaling + height: 48 * scaling + radius: Style.radiusS * scaling + color: Color.mPrimary + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + + RowLayout { + id: widgetContent + anchors.centerIn: parent + spacing: Style.marginXS * scaling + + NIconButton { + icon: "chevron_left" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + enabled: index > 0 + onClicked: { + if (index > 0) { + reorderWidgetInSection("center", index, index - 1) + } + } + } + + NText { + text: modelData + font.pointSize: Style.fontSizeS * scaling + color: Color.mOnPrimary + horizontalAlignment: Text.AlignHCenter + } + + NIconButton { + icon: "chevron_right" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + enabled: index < Settings.data.bar.widgets.center.length - 1 + onClicked: { + if (index < Settings.data.bar.widgets.center.length - 1) { + reorderWidgetInSection("center", index, index + 1) + } + } + } + + NIconButton { + icon: "close" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + onClicked: { + removeWidgetFromSection("center", index) + } + } + } + } + } + } + } + } + + // Right Section + NCard { + Layout.fillWidth: true + Layout.minimumHeight: { + var widgetCount = Settings.data.bar.widgets.right.length + if (widgetCount === 0) return 120 * scaling + + var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins + var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing + var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) + var rows = Math.ceil(widgetCount / widgetsPerRow) + + // Header (40) + spacing (16) + (rows * widget height) + (rows-1 * spacing) + bottom margin (16) + return (40 + 16 + (rows * 48) + ((rows - 1) * Style.marginS) + 16) * scaling + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Style.marginM * scaling + spacing: Style.marginM * scaling + + RowLayout { + Layout.fillWidth: true + + NText { + text: "Right Section" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + + Item { Layout.fillWidth: true } + + Rectangle { + width: 32 * scaling + height: 32 * scaling + radius: width * 0.5 + color: Color.mPrimary + border.color: Color.mPrimaryContainer + border.width: 2 * scaling + + NIcon { + anchors.centerIn: parent + text: "add" + color: Color.mOnPrimary + font.pointSize: Style.fontSizeM * scaling + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + addWidgetDialog.show("right") + } + } + } + } + + Flow { + id: rightWidgetsFlow + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: 52 * scaling + spacing: Style.marginS * scaling + flow: Flow.LeftToRight + + Repeater { + model: Settings.data.bar.widgets.right + delegate: Rectangle { + width: widgetContent.implicitWidth + 16 * scaling + height: 48 * scaling + radius: Style.radiusS * scaling + color: Color.mPrimary + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + + RowLayout { + id: widgetContent + anchors.centerIn: parent + spacing: Style.marginXS * scaling + + NIconButton { + icon: "chevron_left" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + enabled: index > 0 + onClicked: { + if (index > 0) { + reorderWidgetInSection("right", index, index - 1) + } + } + } + + NText { + text: modelData + font.pointSize: Style.fontSizeS * scaling + color: Color.mOnPrimary + horizontalAlignment: Text.AlignHCenter + } + + NIconButton { + icon: "chevron_right" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + enabled: index < Settings.data.bar.widgets.right.length - 1 + onClicked: { + if (index < Settings.data.bar.widgets.right.length - 1) { + reorderWidgetInSection("right", index, index + 1) + } + } + } + + NIconButton { + icon: "close" + size: 20 * scaling + colorBg: Color.applyOpacity(Color.mOnPrimary, "20") + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + onClicked: { + removeWidgetFromSection("right", index) + } + } + } + } + } + } + } + } + } + } } } } + + // Add Widget Dialog + Rectangle { + id: addWidgetDialog + anchors.fill: parent + color: Color.applyOpacity(Color.mShadow, "80") + visible: false + z: 1000 + + property string targetSection: "" + + function show(section) { + targetSection = section + visible = true + } + + function hide() { + visible = false + targetSection = "" + } + + MouseArea { + anchors.fill: parent + onClicked: addWidgetDialog.hide() + } + + Rectangle { + anchors.centerIn: parent + width: 400 * scaling + height: 500 * scaling + radius: Style.radiusL * scaling + color: Color.mSurface + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + + ColumnLayout { + anchors.fill: parent + anchors.margins: Style.marginL * scaling + spacing: Style.marginM * scaling + + NText { + text: "Add Widget to " + (addWidgetDialog.targetSection === "left" ? "Left" : + addWidgetDialog.targetSection === "center" ? "Center" : "Right") + " Section" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + Layout.fillWidth: true + } + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + spacing: Style.marginXS * scaling + + model: ListModel { + ListElement { name: "SystemMonitor"; icon: "memory"; description: "System statistics" } + ListElement { name: "ActiveWindow"; icon: "web_asset"; description: "Active window title" } + ListElement { name: "MediaMini"; icon: "music_note"; description: "Media controls" } + ListElement { name: "Workspace"; icon: "dashboard"; description: "Workspace switcher" } + ListElement { name: "ScreenRecorderIndicator"; icon: "videocam"; description: "Recording indicator" } + ListElement { name: "Tray"; icon: "apps"; description: "System tray" } + ListElement { name: "NotificationHistory"; icon: "notifications"; description: "Notification history" } + ListElement { name: "WiFi"; icon: "wifi"; description: "WiFi status" } + ListElement { name: "Bluetooth"; icon: "bluetooth"; description: "Bluetooth status" } + ListElement { name: "Battery"; icon: "battery_full"; description: "Battery status" } + ListElement { name: "Volume"; icon: "volume_up"; description: "Volume control" } + ListElement { name: "Brightness"; icon: "brightness_6"; description: "Brightness control" } + ListElement { name: "Clock"; icon: "schedule"; description: "Clock" } + ListElement { name: "SidePanelToggle"; icon: "widgets"; description: "Side panel toggle" } + } + + delegate: Rectangle { + width: ListView.view.width + height: 48 * scaling + radius: Style.radiusS * scaling + color: mouseArea.containsMouse ? Color.mTertiary : Color.mSurfaceVariant + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + + RowLayout { + anchors.fill: parent + anchors.margins: Style.marginS * scaling + spacing: Style.marginS * scaling + + NIcon { + text: model.icon + color: Color.mOnSurface + font.pointSize: Style.fontSizeM * scaling + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + NText { + text: model.name + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + + NText { + text: model.description + font.pointSize: Style.fontSizeXS * scaling + color: Color.mOnSurfaceVariant + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + addWidgetToSection(model.name, addWidgetDialog.targetSection) + addWidgetDialog.hide() + } + } + } + } + + RowLayout { + Layout.fillWidth: true + + Item { Layout.fillWidth: true } + + NIconButton { + icon: "close" + size: 20 * scaling + color: Color.mOnSurface + onClicked: addWidgetDialog.hide() + } + } + } + } + } + + // Helper functions + function addWidgetToSection(widgetName, section) { + console.log("Adding widget", widgetName, "to section", section) + var sectionArray = Settings.data.bar.widgets[section] + if (sectionArray) { + // Create a new array to avoid modifying the original + var newArray = sectionArray.slice() + newArray.push(widgetName) + console.log("Widget added. New array:", JSON.stringify(newArray)) + + // Assign the new array + Settings.data.bar.widgets[section] = newArray + } + } + + function removeWidgetFromSection(section, index) { + console.log("Removing widget from section", section, "at index", index) + var sectionArray = Settings.data.bar.widgets[section] + if (sectionArray && index >= 0 && index < sectionArray.length) { + // Create a new array to avoid modifying the original + var newArray = sectionArray.slice() + newArray.splice(index, 1) + console.log("Widget removed. New array:", JSON.stringify(newArray)) + + // Assign the new array + Settings.data.bar.widgets[section] = newArray + } + } + + function reorderWidgetInSection(section, fromIndex, toIndex) { + console.log("Reordering widget in section", section, "from", fromIndex, "to", toIndex) + var sectionArray = Settings.data.bar.widgets[section] + if (sectionArray && fromIndex >= 0 && fromIndex < sectionArray.length && + toIndex >= 0 && toIndex < sectionArray.length) { + + // Create a new array to avoid modifying the original + var newArray = sectionArray.slice() + var item = newArray[fromIndex] + newArray.splice(fromIndex, 1) + newArray.splice(toIndex, 0, item) + console.log("Widget reordered. New array:", JSON.stringify(newArray)) + + // Assign the new array + Settings.data.bar.widgets[section] = newArray + } + } } diff --git a/Modules/SettingsPanel/Tabs/BrightnessTab.qml b/Modules/SettingsPanel/Tabs/BrightnessTab.qml index e4758c3..3b61e7b 100644 --- a/Modules/SettingsPanel/Tabs/BrightnessTab.qml +++ b/Modules/SettingsPanel/Tabs/BrightnessTab.qml @@ -6,33 +6,34 @@ import qs.Commons import qs.Services import qs.Widgets -ColumnLayout { - id: root - spacing: 0 - +Item { + property real scaling: 1 readonly property string tabIcon: "brightness_6" readonly property string tabLabel: "Brightness" + Layout.fillWidth: true + Layout.fillHeight: true ScrollView { - id: scrollView - Layout.fillWidth: true - Layout.fillHeight: true - padding: Style.marginM * scaling + anchors.fill: parent clip: true - - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.vertical.policy: ScrollBar.AsNeeded + ScrollBar.horizontal.policy: ScrollBar.AsNeeded + contentWidth: parent.width ColumnLayout { - width: scrollView.availableWidth - spacing: 0 - + width: parent.width ColumnLayout { - - width: scrollView.availableWidth spacing: Style.marginL * scaling + Layout.margins: Style.marginL * scaling Layout.fillWidth: true + NText { + text: "Brightness Settings" + font.pointSize: Style.fontSizeXXL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + // Bar Visibility Section NToggle { label: "Show Bar Icon" diff --git a/Modules/SettingsPanel/Tabs/ColorSchemeTab.qml b/Modules/SettingsPanel/Tabs/ColorSchemeTab.qml index 80da36a..c7343b6 100644 --- a/Modules/SettingsPanel/Tabs/ColorSchemeTab.qml +++ b/Modules/SettingsPanel/Tabs/ColorSchemeTab.qml @@ -163,28 +163,22 @@ ColumnLayout { Layout.bottomMargin: Style.marginL * scaling } - NText { - text: "Predefined Color Schemes" - font.pointSize: Style.fontSizeXXL * scaling - font.weight: Style.fontWeightBold - color: Color.mOnSurface - } - ColumnLayout { spacing: Style.marginXXS * scaling Layout.fillWidth: true - // NText { - // text: "Predefined Color Schemes" - // font.pointSize: Style.fontSizeL * scaling - // font.weight: Style.fontWeightBold - // color: Color.mOnSurface - // Layout.fillWidth: true - // } + NText { + text: "Predefined Color Schemes" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + Layout.fillWidth: true + } + NText { text: "These color schemes only apply when 'Use Matugen' is disabled. When enabled, Matugen will generate colors based on your wallpaper instead. You can toggle between light and dark themes when using Matugen." font.pointSize: Style.fontSizeXS * scaling - color: Color.mOnSurfaceVariant + color: Color.mOnSurface Layout.fillWidth: true wrapMode: Text.WordWrap } diff --git a/Modules/SettingsPanel/Tabs/GeneralTab.qml b/Modules/SettingsPanel/Tabs/GeneralTab.qml index 19da93f..1cb1878 100644 --- a/Modules/SettingsPanel/Tabs/GeneralTab.qml +++ b/Modules/SettingsPanel/Tabs/GeneralTab.qml @@ -33,6 +33,13 @@ ColumnLayout { spacing: Style.marginL * scaling Layout.fillWidth: true + NText { + text: "General Settings" + font.pointSize: Style.fontSizeXXL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + // Profile section ColumnLayout { spacing: Style.marginS * scaling diff --git a/Modules/SettingsPanel/Tabs/LauncherTab.qml b/Modules/SettingsPanel/Tabs/LauncherTab.qml index 5d9d6aa..ae10ade 100644 --- a/Modules/SettingsPanel/Tabs/LauncherTab.qml +++ b/Modules/SettingsPanel/Tabs/LauncherTab.qml @@ -34,7 +34,7 @@ ColumnLayout { Layout.fillWidth: true NText { - text: "Launcer Options" + text: "Launcher" font.pointSize: Style.fontSizeXXL * scaling font.weight: Style.fontWeightBold color: Color.mOnSurface @@ -56,7 +56,7 @@ ColumnLayout { } NText { - text: "Launcer Anchoring" + text: "Launcher Position" font.pointSize: Style.fontSizeXXL * scaling font.weight: Style.fontWeightBold color: Color.mOnSurface diff --git a/Modules/SettingsPanel/Tabs/NetworkTab.qml b/Modules/SettingsPanel/Tabs/NetworkTab.qml index f621f79..7029ecb 100644 --- a/Modules/SettingsPanel/Tabs/NetworkTab.qml +++ b/Modules/SettingsPanel/Tabs/NetworkTab.qml @@ -34,6 +34,13 @@ ColumnLayout { spacing: Style.marginL * scaling Layout.fillWidth: true + NText { + text: "Interfaces" + font.pointSize: Style.fontSizeXXL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + } + NToggle { label: "WiFi Enabled" description: "Enable WiFi connectivity." diff --git a/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml b/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml index a02b3a3..836ca6c 100644 --- a/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml +++ b/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml @@ -33,6 +33,14 @@ ColumnLayout { spacing: Style.marginXS * scaling Layout.fillWidth: true + NText { + text: "Recordings" + font.pointSize: Style.fontSizeXXL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + Layout.bottomMargin: Style.marginS * scaling + } + // Output Directory ColumnLayout { spacing: Style.marginS * scaling diff --git a/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml b/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml index f3783a5..4e7cd52 100644 --- a/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml +++ b/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml @@ -33,6 +33,14 @@ ColumnLayout { spacing: Style.marginXS * scaling Layout.fillWidth: true + NText { + text: "Location" + font.pointSize: Style.fontSizeXXL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + Layout.bottomMargin: Style.marginS * scaling + } + // Location section ColumnLayout { spacing: Style.marginM * scaling diff --git a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml index b3112e8..79b7fa2 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml @@ -31,7 +31,7 @@ Item { // Current wallpaper display NText { text: "Current Wallpaper" - font.pointSize: Style.fontSizeL * scaling + font.pointSize: Style.fontSizeXXL * scaling font.weight: Style.fontWeightBold color: Color.mOnSurface } diff --git a/Modules/SettingsPanel/Tabs/WallpaperTab.qml b/Modules/SettingsPanel/Tabs/WallpaperTab.qml index 8fe156d..f8da98d 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperTab.qml @@ -34,6 +34,14 @@ ColumnLayout { spacing: Style.marginL * scaling Layout.fillWidth: true + NText { + text: "Directory" + font.pointSize: Style.fontSizeXXL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + Layout.bottomMargin: Style.marginS * scaling + } + // Wallpaper Settings Category ColumnLayout { spacing: Style.marginS * scaling From 12092ca6f640b303d5d9b783d07e7b089b05a6d8 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 17:00:39 +0200 Subject: [PATCH 02/10] Fix Brightness & Tray --- Commons/Color.qml | 1 + Modules/Bar/Bar.qml | 5 ++++- Modules/Bar/Widgets/Brightness.qml | 2 +- Modules/Bar/Widgets/Tray.qml | 17 +++++++++++------ Modules/SettingsPanel/Tabs/BarTab.qml | 11 ++++++----- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Commons/Color.qml b/Commons/Color.qml index 221716f..f31f1ac 100644 --- a/Commons/Color.qml +++ b/Commons/Color.qml @@ -47,6 +47,7 @@ Singleton { // ----------- function applyOpacity(color, opacity) { // Convert color to string and apply opacity + if (!color) return "transparent" return color.toString().replace("#", "#" + opacity) } diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 61e7940..c99cfda 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -137,13 +137,16 @@ Variants { return null } + console.log(`Attempting to load widget: ${widgetName}.qml`) + // Try to load the widget directly from file const component = Qt.createComponent(`../Bar/Widgets/${widgetName}.qml`) if (component.status === Component.Ready) { + console.log(`Successfully created component for: ${widgetName}.qml`) return component } - console.warn(`Failed to load widget: ${widgetName}.qml`) + console.warn(`Failed to load widget: ${widgetName}.qml, status:`, component.status, "error:", component.errorString()) return null } diff --git a/Modules/Bar/Widgets/Brightness.qml b/Modules/Bar/Widgets/Brightness.qml index 82bacb8..58c8cc6 100644 --- a/Modules/Bar/Widgets/Brightness.qml +++ b/Modules/Bar/Widgets/Brightness.qml @@ -10,7 +10,7 @@ Item { width: pill.width height: pill.height - visible: firstBrightnessReceived && getMonitor() !== null + visible: getMonitor() !== null // Used to avoid opening the pill on Quickshell startup property bool firstBrightnessReceived: false diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index c6ba8c9..9998fe6 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -9,10 +9,14 @@ import qs.Commons import qs.Services import qs.Widgets + + Rectangle { readonly property real itemSize: 24 * scaling visible: Settings.data.bar.showTray && (SystemTray.items.values.length > 0) + + width: tray.width + Style.marginM * scaling * 2 height: Math.round(Style.capsuleHeight * scaling) @@ -95,14 +99,14 @@ Rectangle { return } - if (modelData.hasMenu && modelData.menu && trayMenu) { + if (modelData.hasMenu && modelData.menu && trayMenu.item) { trayPanel.open() // Anchor the menu to the tray icon item (parent) and position it below the icon - const menuX = (width / 2) - (trayMenu.width / 2) + const menuX = (width / 2) - (trayMenu.item.width / 2) const menuY = (Style.barHeight * scaling) - trayMenu.menu = modelData.menu - trayMenu.showAt(parent, menuX, menuY) + trayMenu.item.menu = modelData.menu + trayMenu.item.showAt(parent, menuX, menuY) } else { Logger.log("Tray", "No menu available for", modelData.id, "or trayMenu not set") } @@ -142,7 +146,7 @@ Rectangle { function close() { visible = false - trayMenu.hideMenu() + trayMenu.item.hideMenu() } // Clicking outside of the rectangle to close @@ -151,8 +155,9 @@ Rectangle { onClicked: trayPanel.close() } - TrayMenu { + Loader { id: trayMenu + source: "TrayMenu.qml" } } } diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index d03c333..307db25 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -199,7 +199,7 @@ ColumnLayout { height: 32 * scaling radius: width * 0.5 color: Color.mPrimary - border.color: Color.mPrimaryContainer + border.color: Color.mPrimary border.width: 2 * scaling NIcon { @@ -336,7 +336,7 @@ ColumnLayout { height: 32 * scaling radius: width * 0.5 color: Color.mPrimary - border.color: Color.mPrimaryContainer + border.color: Color.mPrimary border.width: 2 * scaling NIcon { @@ -473,7 +473,7 @@ ColumnLayout { height: 32 * scaling radius: width * 0.5 color: Color.mPrimary - border.color: Color.mPrimaryContainer + border.color: Color.mPrimary border.width: 2 * scaling NIcon { @@ -612,8 +612,9 @@ ColumnLayout { border.width: Math.max(1, Style.borderS * scaling) ColumnLayout { - anchors.fill: parent - anchors.margins: Style.marginL * scaling + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: Style.marginL * scaling spacing: Style.marginM * scaling NText { From f441bec32db02024669ba08ba89d11a910f2e474 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 18:13:18 +0200 Subject: [PATCH 03/10] Fix Layout, more changes to modular bar --- Modules/Bar/Bar.qml | 64 +++++- Modules/Bar/Widgets/Bluetooth.qml | 26 ++- Modules/Bar/Widgets/WiFi.qml | 62 +++++- Modules/SettingsPanel/Tabs/BarTab.qml | 293 +++++++------------------- Widgets/NComboBox.qml | 4 +- 5 files changed, 212 insertions(+), 237 deletions(-) diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index c99cfda..462b8c6 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -23,6 +23,10 @@ Variants { implicitHeight: Style.barHeight * scaling color: Color.transparent + Component.onCompleted: { + logWidgetLoadingSummary() + } + // If no bar activated in settings, then show them all visible: modelData ? (Settings.data.bar.monitors.includes(modelData.name) || (Settings.data.bar.monitors.length === 0)) : false @@ -66,9 +70,10 @@ Variants { anchors.verticalCenter: parent.verticalCenter onStatusChanged: { if (status === Loader.Error) { - console.warn(`Failed to load widget: ${modelData}`) + Logger.error("Bar", `Failed to load ${modelData} widget`) + onWidgetFailed() } else if (status === Loader.Ready) { - console.log(`Successfully loaded widget: ${modelData}`) + onWidgetLoaded() } } } @@ -93,9 +98,10 @@ Variants { anchors.verticalCenter: parent.verticalCenter onStatusChanged: { if (status === Loader.Error) { - console.warn(`Failed to load widget: ${modelData}`) + Logger.error("Bar", `Failed to load ${modelData} widget`) + onWidgetFailed() } else if (status === Loader.Ready) { - console.log(`Successfully loaded widget: ${modelData}`) + onWidgetLoaded() } } } @@ -121,9 +127,10 @@ Variants { anchors.verticalCenter: parent.verticalCenter onStatusChanged: { if (status === Loader.Error) { - console.warn(`Failed to load widget: ${modelData}`) + Logger.error("Bar", `Failed to load ${modelData} widget`) + onWidgetFailed() } else if (status === Loader.Ready) { - console.log(`Successfully loaded widget: ${modelData}`) + onWidgetLoaded() } } } @@ -137,19 +144,56 @@ Variants { return null } - console.log(`Attempting to load widget: ${widgetName}.qml`) + const widgetPath = `../Bar/Widgets/${widgetName}.qml` + Logger.log("Bar", `Attempting to load widget from: ${widgetPath}`) // Try to load the widget directly from file - const component = Qt.createComponent(`../Bar/Widgets/${widgetName}.qml`) + const component = Qt.createComponent(widgetPath) if (component.status === Component.Ready) { - console.log(`Successfully created component for: ${widgetName}.qml`) + Logger.log("Bar", `Successfully created component for: ${widgetName}.qml`) return component } - console.warn(`Failed to load widget: ${widgetName}.qml, status:`, component.status, "error:", component.errorString()) + Logger.error("Bar", `Failed to load ${widgetName}.qml widget, status: ${component.status}, error: ${component.errorString()}`) return null } + // Track widget loading status + property int totalWidgets: 0 + property int loadedWidgets: 0 + property int failedWidgets: 0 + + // Log widget loading summary + function logWidgetLoadingSummary() { + const allWidgets = [ + ...Settings.data.bar.widgets.left, + ...Settings.data.bar.widgets.center, + ...Settings.data.bar.widgets.right + ] + + totalWidgets = allWidgets.length + loadedWidgets = 0 + failedWidgets = 0 + + if (totalWidgets > 0) { + Logger.log("Bar", `Attempting to load ${totalWidgets} widgets`) + } + } + + function onWidgetLoaded() { + loadedWidgets++ + if (loadedWidgets + failedWidgets === totalWidgets) { + Logger.log("Bar", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) + } + } + + function onWidgetFailed() { + failedWidgets++ + if (loadedWidgets + failedWidgets === totalWidgets) { + Logger.log("Bar", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) + } + } + diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index 09c5c60..90b8dee 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -7,6 +7,7 @@ import qs.Commons import qs.Services import qs.Widgets + NIconButton { id: root @@ -32,7 +33,30 @@ NIconButton { bluetoothPanel.toggle(screen) } - BluetoothPanel { + Loader { id: bluetoothPanel + source: "BluetoothPanel.qml" + active: false + + property var pendingToggleScreen: null + + onStatusChanged: { + if (status === Loader.Ready && item && pendingToggleScreen !== null) { + item.toggle(pendingToggleScreen) + pendingToggleScreen = null + } + } + + function toggle(screen) { + // Load the panel if it's not already loaded + if (!active) { + active = true + pendingToggleScreen = screen + } else if (status === Loader.Ready && item) { + item.toggle(screen) + } else { + pendingToggleScreen = screen + } + } } } diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml index cdae9be..61a6a13 100644 --- a/Modules/Bar/Widgets/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -7,34 +7,76 @@ import qs.Commons import qs.Services import qs.Widgets + NIconButton { id: root sizeMultiplier: 0.8 + Component.onCompleted: { + Logger.log("WiFi", "Widget component completed") + Logger.log("WiFi", "NetworkService available:", !!NetworkService) + if (NetworkService) { + Logger.log("WiFi", "NetworkService.networks available:", !!NetworkService.networks) + } + } + colorBg: Color.mSurfaceVariant colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent icon: { - let connected = false - let signalStrength = 0 - for (const net in NetworkService.networks) { - if (NetworkService.networks[net].connected) { - connected = true - signalStrength = NetworkService.networks[net].signal - break + try { + let connected = false + let signalStrength = 0 + for (const net in NetworkService.networks) { + if (NetworkService.networks[net].connected) { + connected = true + signalStrength = NetworkService.networks[net].signal + break + } } + return connected ? NetworkService.signalIcon(signalStrength) : "wifi" + } catch (error) { + Logger.error("WiFi", "Error getting icon:", error) + return "wifi" } - return connected ? NetworkService.signalIcon(signalStrength) : "wifi" } tooltipText: "WiFi Networks" onClicked: { - wifiPanel.toggle(screen) + try { + Logger.log("WiFi", "Button clicked, toggling panel") + wifiPanel.toggle(screen) + } catch (error) { + Logger.error("WiFi", "Error toggling panel:", error) + } } - WiFiPanel { + Loader { id: wifiPanel + source: "WiFiPanel.qml" + active: false + + property var pendingToggleScreen: null + + onStatusChanged: { + if (status === Loader.Ready && item && pendingToggleScreen !== null) { + item.toggle(pendingToggleScreen) + pendingToggleScreen = null + } + } + + function toggle(screen) { + // Load the panel if it's not already loaded + if (!active) { + active = true + pendingToggleScreen = screen + } else if (status === Loader.Ready && item) { + item.toggle(screen) + } else { + pendingToggleScreen = screen + } + } } } diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index 307db25..a44e908 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -166,15 +166,15 @@ ColumnLayout { Layout.fillWidth: true Layout.minimumHeight: { var widgetCount = Settings.data.bar.widgets.left.length - if (widgetCount === 0) return 120 * scaling + if (widgetCount === 0) return 140 * scaling var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) var rows = Math.ceil(widgetCount / widgetsPerRow) - // Header (40) + spacing (16) + (rows * widget height) + (rows-1 * spacing) + bottom margin (16) - return (40 + 16 + (rows * 48) + ((rows - 1) * Style.marginS) + 16) * scaling + // Header (50) + spacing (20) + (rows * widget height) + (rows-1 * spacing) + bottom margin (20) + return (50 + 20 + (rows * 48) + ((rows - 1) * Style.marginS) + 20) * scaling } ColumnLayout { @@ -194,28 +194,14 @@ ColumnLayout { Item { Layout.fillWidth: true } - Rectangle { - width: 32 * scaling - height: 32 * scaling - radius: width * 0.5 - color: Color.mPrimary - border.color: Color.mPrimary - border.width: 2 * scaling - - NIcon { - anchors.centerIn: parent - text: "add" - color: Color.mOnPrimary - font.pointSize: Style.fontSizeM * scaling - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - addWidgetDialog.show("left") - } + NComboBox { + width: 120 * scaling + model: availableWidgets + label: "" + description: "" + placeholder: "Add widget to left section" + onSelected: key => { + addWidgetToSection(key, "left") } } } @@ -224,7 +210,7 @@ ColumnLayout { id: leftWidgetsFlow Layout.fillWidth: true Layout.fillHeight: true - Layout.minimumHeight: 52 * scaling + Layout.minimumHeight: 65 * scaling spacing: Style.marginS * scaling flow: Flow.LeftToRight @@ -303,15 +289,15 @@ ColumnLayout { Layout.fillWidth: true Layout.minimumHeight: { var widgetCount = Settings.data.bar.widgets.center.length - if (widgetCount === 0) return 120 * scaling + if (widgetCount === 0) return 140 * scaling var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) var rows = Math.ceil(widgetCount / widgetsPerRow) - // Header (40) + spacing (16) + (rows * widget height) + (rows-1 * spacing) + bottom margin (16) - return (40 + 16 + (rows * 48) + ((rows - 1) * Style.marginS) + 16) * scaling + // Header (50) + spacing (20) + (rows * widget height) + (rows-1 * spacing) + bottom margin (20) + return (50 + 20 + (rows * 48) + ((rows - 1) * Style.marginS) + 20) * scaling } ColumnLayout { @@ -331,28 +317,14 @@ ColumnLayout { Item { Layout.fillWidth: true } - Rectangle { - width: 32 * scaling - height: 32 * scaling - radius: width * 0.5 - color: Color.mPrimary - border.color: Color.mPrimary - border.width: 2 * scaling - - NIcon { - anchors.centerIn: parent - text: "add" - color: Color.mOnPrimary - font.pointSize: Style.fontSizeM * scaling - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - addWidgetDialog.show("center") - } + NComboBox { + width: 120 * scaling + model: availableWidgets + label: "" + description: "" + placeholder: "Add widget to center section" + onSelected: key => { + addWidgetToSection(key, "center") } } } @@ -361,7 +333,7 @@ ColumnLayout { id: centerWidgetsFlow Layout.fillWidth: true Layout.fillHeight: true - Layout.minimumHeight: 52 * scaling + Layout.minimumHeight: 65 * scaling spacing: Style.marginS * scaling flow: Flow.LeftToRight @@ -440,15 +412,15 @@ ColumnLayout { Layout.fillWidth: true Layout.minimumHeight: { var widgetCount = Settings.data.bar.widgets.right.length - if (widgetCount === 0) return 120 * scaling + if (widgetCount === 0) return 140 * scaling var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) var rows = Math.ceil(widgetCount / widgetsPerRow) - // Header (40) + spacing (16) + (rows * widget height) + (rows-1 * spacing) + bottom margin (16) - return (40 + 16 + (rows * 48) + ((rows - 1) * Style.marginS) + 16) * scaling + // Header (50) + spacing (20) + (rows * widget height) + (rows-1 * spacing) + bottom margin (20) + return (50 + 20 + (rows * 48) + ((rows - 1) * Style.marginS) + 20) * scaling } ColumnLayout { @@ -468,28 +440,14 @@ ColumnLayout { Item { Layout.fillWidth: true } - Rectangle { - width: 32 * scaling - height: 32 * scaling - radius: width * 0.5 - color: Color.mPrimary - border.color: Color.mPrimary - border.width: 2 * scaling - - NIcon { - anchors.centerIn: parent - text: "add" - color: Color.mOnPrimary - font.pointSize: Style.fontSizeM * scaling - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - addWidgetDialog.show("right") - } + NComboBox { + width: 120 * scaling + model: availableWidgets + label: "" + description: "" + placeholder: "Add widget to right section" + onSelected: key => { + addWidgetToSection(key, "right") } } } @@ -498,7 +456,7 @@ ColumnLayout { id: rightWidgetsFlow Layout.fillWidth: true Layout.fillHeight: true - Layout.minimumHeight: 52 * scaling + Layout.minimumHeight: 65 * scaling spacing: Style.marginS * scaling flow: Flow.LeftToRight @@ -577,144 +535,7 @@ ColumnLayout { } } - // Add Widget Dialog - Rectangle { - id: addWidgetDialog - anchors.fill: parent - color: Color.applyOpacity(Color.mShadow, "80") - visible: false - z: 1000 - property string targetSection: "" - - function show(section) { - targetSection = section - visible = true - } - - function hide() { - visible = false - targetSection = "" - } - - MouseArea { - anchors.fill: parent - onClicked: addWidgetDialog.hide() - } - - Rectangle { - anchors.centerIn: parent - width: 400 * scaling - height: 500 * scaling - radius: Style.radiusL * scaling - color: Color.mSurface - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS * scaling) - - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.margins: Style.marginL * scaling - spacing: Style.marginM * scaling - - NText { - text: "Add Widget to " + (addWidgetDialog.targetSection === "left" ? "Left" : - addWidgetDialog.targetSection === "center" ? "Center" : "Right") + " Section" - font.pointSize: Style.fontSizeL * scaling - font.weight: Style.fontWeightBold - color: Color.mOnSurface - Layout.fillWidth: true - } - - ListView { - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - spacing: Style.marginXS * scaling - - model: ListModel { - ListElement { name: "SystemMonitor"; icon: "memory"; description: "System statistics" } - ListElement { name: "ActiveWindow"; icon: "web_asset"; description: "Active window title" } - ListElement { name: "MediaMini"; icon: "music_note"; description: "Media controls" } - ListElement { name: "Workspace"; icon: "dashboard"; description: "Workspace switcher" } - ListElement { name: "ScreenRecorderIndicator"; icon: "videocam"; description: "Recording indicator" } - ListElement { name: "Tray"; icon: "apps"; description: "System tray" } - ListElement { name: "NotificationHistory"; icon: "notifications"; description: "Notification history" } - ListElement { name: "WiFi"; icon: "wifi"; description: "WiFi status" } - ListElement { name: "Bluetooth"; icon: "bluetooth"; description: "Bluetooth status" } - ListElement { name: "Battery"; icon: "battery_full"; description: "Battery status" } - ListElement { name: "Volume"; icon: "volume_up"; description: "Volume control" } - ListElement { name: "Brightness"; icon: "brightness_6"; description: "Brightness control" } - ListElement { name: "Clock"; icon: "schedule"; description: "Clock" } - ListElement { name: "SidePanelToggle"; icon: "widgets"; description: "Side panel toggle" } - } - - delegate: Rectangle { - width: ListView.view.width - height: 48 * scaling - radius: Style.radiusS * scaling - color: mouseArea.containsMouse ? Color.mTertiary : Color.mSurfaceVariant - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS * scaling) - - RowLayout { - anchors.fill: parent - anchors.margins: Style.marginS * scaling - spacing: Style.marginS * scaling - - NIcon { - text: model.icon - color: Color.mOnSurface - font.pointSize: Style.fontSizeM * scaling - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 0 - - NText { - text: model.name - font.pointSize: Style.fontSizeS * scaling - font.weight: Style.fontWeightBold - color: Color.mOnSurface - } - - NText { - text: model.description - font.pointSize: Style.fontSizeXS * scaling - color: Color.mOnSurfaceVariant - } - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - addWidgetToSection(model.name, addWidgetDialog.targetSection) - addWidgetDialog.hide() - } - } - } - } - - RowLayout { - Layout.fillWidth: true - - Item { Layout.fillWidth: true } - - NIconButton { - icon: "close" - size: 20 * scaling - color: Color.mOnSurface - onClicked: addWidgetDialog.hide() - } - } - } - } - } // Helper functions function addWidgetToSection(widgetName, section) { @@ -762,4 +583,46 @@ ColumnLayout { Settings.data.bar.widgets[section] = newArray } } + + // Widget list for adding widgets + ListModel { + id: availableWidgets + ListElement { key: "SystemMonitor"; name: "SystemMonitor" } + ListElement { key: "ActiveWindow"; name: "ActiveWindow" } + ListElement { key: "MediaMini"; name: "MediaMini" } + ListElement { key: "Workspace"; name: "Workspace" } + ListElement { key: "ScreenRecorderIndicator"; name: "ScreenRecorderIndicator" } + ListElement { key: "Tray"; name: "Tray" } + ListElement { key: "NotificationHistory"; name: "NotificationHistory" } + ListElement { key: "WiFi"; name: "WiFi" } + ListElement { key: "Bluetooth"; name: "Bluetooth" } + ListElement { key: "Battery"; name: "Battery" } + ListElement { key: "Volume"; name: "Volume" } + ListElement { key: "Brightness"; name: "Brightness" } + ListElement { key: "Clock"; name: "Clock" } + ListElement { key: "SidePanelToggle"; name: "SidePanelToggle" } + } + + + + // Helper function to get widget icons + function getWidgetIcon(widgetKey) { + switch(widgetKey) { + case "SystemMonitor": return "memory" + case "ActiveWindow": return "web_asset" + case "MediaMini": return "music_note" + case "Workspace": return "dashboard" + case "ScreenRecorderIndicator": return "videocam" + case "Tray": return "apps" + case "NotificationHistory": return "notifications" + case "WiFi": return "wifi" + case "Bluetooth": return "bluetooth" + case "Battery": return "battery_full" + case "Volume": return "volume_up" + case "Brightness": return "brightness_6" + case "Clock": return "schedule" + case "SidePanelToggle": return "widgets" + default: return "widgets" + } + } } diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml index 9ec2aaf..c82abb0 100644 --- a/Widgets/NComboBox.qml +++ b/Widgets/NComboBox.qml @@ -16,6 +16,7 @@ ColumnLayout { } property string currentKey: '' + property string placeholder: "" signal selected(string key) @@ -61,8 +62,9 @@ ColumnLayout { font.pointSize: Style.fontSizeM * scaling verticalAlignment: Text.AlignVCenter elide: Text.ElideRight + color: (combo.currentIndex >= 0 && combo.currentIndex < root.model.count) ? Color.mOnSurface : Color.mOnSurfaceVariant text: (combo.currentIndex >= 0 && combo.currentIndex < root.model.count) ? root.model.get( - combo.currentIndex).name : "" + combo.currentIndex).name : root.placeholder } indicator: NIcon { From 8bb6da5e0ddd8530b082be097adf67720b6ff315 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Fri, 22 Aug 2025 12:25:28 -0400 Subject: [PATCH 04/10] Fix BT and WiFi panels --- Modules/Bar/Widgets/Bluetooth.qml | 27 ------------------ Modules/Bar/Widgets/WiFi.qml | 28 ------------------- .../BluetoothPanel.qml | 0 .../{Bar/Widgets => WiFiPanel}/WiFiPanel.qml | 0 shell.qml | 11 +++++++- 5 files changed, 10 insertions(+), 56 deletions(-) rename Modules/{Bar/Widgets => BluetoothPanel}/BluetoothPanel.qml (100%) rename Modules/{Bar/Widgets => WiFiPanel}/WiFiPanel.qml (100%) diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index 90b8dee..35ff4d5 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -32,31 +32,4 @@ NIconButton { onClicked: { bluetoothPanel.toggle(screen) } - - Loader { - id: bluetoothPanel - source: "BluetoothPanel.qml" - active: false - - property var pendingToggleScreen: null - - onStatusChanged: { - if (status === Loader.Ready && item && pendingToggleScreen !== null) { - item.toggle(pendingToggleScreen) - pendingToggleScreen = null - } - } - - function toggle(screen) { - // Load the panel if it's not already loaded - if (!active) { - active = true - pendingToggleScreen = screen - } else if (status === Loader.Ready && item) { - item.toggle(screen) - } else { - pendingToggleScreen = screen - } - } - } } diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml index 61a6a13..83b4689 100644 --- a/Modules/Bar/Widgets/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -7,7 +7,6 @@ import qs.Commons import qs.Services import qs.Widgets - NIconButton { id: root @@ -52,31 +51,4 @@ NIconButton { Logger.error("WiFi", "Error toggling panel:", error) } } - - Loader { - id: wifiPanel - source: "WiFiPanel.qml" - active: false - - property var pendingToggleScreen: null - - onStatusChanged: { - if (status === Loader.Ready && item && pendingToggleScreen !== null) { - item.toggle(pendingToggleScreen) - pendingToggleScreen = null - } - } - - function toggle(screen) { - // Load the panel if it's not already loaded - if (!active) { - active = true - pendingToggleScreen = screen - } else if (status === Loader.Ready && item) { - item.toggle(screen) - } else { - pendingToggleScreen = screen - } - } - } } diff --git a/Modules/Bar/Widgets/BluetoothPanel.qml b/Modules/BluetoothPanel/BluetoothPanel.qml similarity index 100% rename from Modules/Bar/Widgets/BluetoothPanel.qml rename to Modules/BluetoothPanel/BluetoothPanel.qml diff --git a/Modules/Bar/Widgets/WiFiPanel.qml b/Modules/WiFiPanel/WiFiPanel.qml similarity index 100% rename from Modules/Bar/Widgets/WiFiPanel.qml rename to Modules/WiFiPanel/WiFiPanel.qml diff --git a/shell.qml b/shell.qml index bd52508..8ce8361 100644 --- a/shell.qml +++ b/shell.qml @@ -16,6 +16,7 @@ import qs.Commons import qs.Modules.Launcher import qs.Modules.Background import qs.Modules.Bar +import qs.Modules.BluetoothPanel import qs.Modules.Calendar import qs.Modules.Dock import qs.Modules.IPC @@ -25,7 +26,7 @@ import qs.Modules.SettingsPanel import qs.Modules.PowerPanel import qs.Modules.SidePanel import qs.Modules.Toast - +import qs.Modules.WiFiPanel import qs.Services import qs.Widgets @@ -70,6 +71,14 @@ ShellRoot { id: powerPanel } + WiFiPanel { + id: wifiPanel + } + + BluetoothPanel { + id: bluetoothPanel + } + ToastManager {} IPCManager {} From c70b7004c638de0ae6c645c82549bf7ff10f04f1 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 18:49:12 +0200 Subject: [PATCH 05/10] More fixes for BarTab --- Modules/SettingsPanel/Tabs/BarTab.qml | 118 ++++++++++++++++++++++---- 1 file changed, 103 insertions(+), 15 deletions(-) diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index a44e908..525acb7 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import Qt.labs.folderlistmodel import qs.Commons import qs.Services import qs.Widgets @@ -584,23 +585,110 @@ ColumnLayout { } } - // Widget list for adding widgets + // Dynamic widget discovery using FolderListModel + FolderListModel { + id: widgetFolderModel + folder: Qt.resolvedUrl("../../Bar/Widgets/") + nameFilters: ["*.qml"] + showDirs: false + showFiles: true + } + ListModel { id: availableWidgets - ListElement { key: "SystemMonitor"; name: "SystemMonitor" } - ListElement { key: "ActiveWindow"; name: "ActiveWindow" } - ListElement { key: "MediaMini"; name: "MediaMini" } - ListElement { key: "Workspace"; name: "Workspace" } - ListElement { key: "ScreenRecorderIndicator"; name: "ScreenRecorderIndicator" } - ListElement { key: "Tray"; name: "Tray" } - ListElement { key: "NotificationHistory"; name: "NotificationHistory" } - ListElement { key: "WiFi"; name: "WiFi" } - ListElement { key: "Bluetooth"; name: "Bluetooth" } - ListElement { key: "Battery"; name: "Battery" } - ListElement { key: "Volume"; name: "Volume" } - ListElement { key: "Brightness"; name: "Brightness" } - ListElement { key: "Clock"; name: "Clock" } - ListElement { key: "SidePanelToggle"; name: "SidePanelToggle" } + } + + Component.onCompleted: { + discoverWidgets() + } + + // Automatically discover available widgets from the Widgets directory + function discoverWidgets() { + console.log("Discovering widgets...") + console.log("FolderListModel count:", widgetFolderModel.count) + console.log("FolderListModel folder:", widgetFolderModel.folder) + + availableWidgets.clear() + + // Process each .qml file found in the directory + for (let i = 0; i < widgetFolderModel.count; i++) { + const fileName = widgetFolderModel.get(i, "fileName") + console.log("Found file:", fileName) + const widgetName = fileName.replace('.qml', '') + + // Skip TrayMenu as it's not a standalone widget + if (widgetName !== 'TrayMenu') { + console.log("Adding widget:", widgetName) + availableWidgets.append({ + key: widgetName, + name: widgetName, + icon: getDefaultIcon(widgetName) + }) + } + } + + console.log("Total widgets added:", availableWidgets.count) + + // If FolderListModel didn't find anything, use fallback + if (availableWidgets.count === 0) { + console.log("FolderListModel failed, using fallback list") + const fallbackWidgets = [ + "ActiveWindow", "Battery", "Bluetooth", "Brightness", "Clock", + "MediaMini", "NotificationHistory", "ScreenRecorderIndicator", + "SidePanelToggle", "SystemMonitor", "Tray", "Volume", "WiFi", "Workspace" + ] + + fallbackWidgets.forEach(widgetName => { + availableWidgets.append({ + key: widgetName, + name: widgetName, + icon: getDefaultIcon(widgetName) + }) + }) + } + + // Sort alphabetically by name + sortWidgets() + } + + // Sort widgets alphabetically + function sortWidgets() { + const widgets = [] + for (let i = 0; i < availableWidgets.count; i++) { + widgets.push({ + key: availableWidgets.get(i).key, + name: availableWidgets.get(i).name, + icon: availableWidgets.get(i).icon + }) + } + + widgets.sort((a, b) => a.name.localeCompare(b.name)) + + availableWidgets.clear() + widgets.forEach(widget => { + availableWidgets.append(widget) + }) + } + + // Get default icon for widget (can be overridden in widget files) + function getDefaultIcon(widgetName) { + const iconMap = { + "ActiveWindow": "web_asset", + "Battery": "battery_full", + "Bluetooth": "bluetooth", + "Brightness": "brightness_6", + "Clock": "schedule", + "MediaMini": "music_note", + "NotificationHistory": "notifications", + "ScreenRecorderIndicator": "videocam", + "SidePanelToggle": "widgets", + "SystemMonitor": "memory", + "Tray": "apps", + "Volume": "volume_up", + "WiFi": "wifi", + "Workspace": "dashboard" + } + return iconMap[widgetName] || "widgets" } From b5ea306b92dd41576cc9ac85b1084eca8b5f3cac Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 18:57:04 +0200 Subject: [PATCH 06/10] Fix Batter Symbol visibility --- Modules/Bar/Widgets/Battery.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/Bar/Widgets/Battery.qml b/Modules/Bar/Widgets/Battery.qml index ef09f18..8349da5 100644 --- a/Modules/Bar/Widgets/Battery.qml +++ b/Modules/Bar/Widgets/Battery.qml @@ -22,8 +22,6 @@ NPill { // Choose icon based on charge and charging state function batteryIcon() { - if (!show) - return "" if (charging) return "battery_android_bolt" From 68181a4df67d7fd137d3fadee1398adb8073854c Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 19:01:02 +0200 Subject: [PATCH 07/10] Move widget logic into it's own file (Commons/WidgetLoader.qml) --- Commons/WidgetLoader.qml | 70 ++++++++++++++++++++++++++++++++ Modules/Bar/Bar.qml | 88 ++++++++++++---------------------------- 2 files changed, 97 insertions(+), 61 deletions(-) create mode 100644 Commons/WidgetLoader.qml diff --git a/Commons/WidgetLoader.qml b/Commons/WidgetLoader.qml new file mode 100644 index 0000000..fda9635 --- /dev/null +++ b/Commons/WidgetLoader.qml @@ -0,0 +1,70 @@ +import QtQuick +import qs.Commons + +QtObject { + id: root + + // Signal emitted when widget loading status changes + signal widgetLoaded(string widgetName) + signal widgetFailed(string widgetName, string error) + signal loadingComplete(int total, int loaded, int failed) + + // Properties to track loading status + property int totalWidgets: 0 + property int loadedWidgets: 0 + property int failedWidgets: 0 + + // Auto-discover widget components + function getWidgetComponent(widgetName) { + if (!widgetName || widgetName.trim() === "") { + return null + } + + const widgetPath = `../Modules/Bar/Widgets/${widgetName}.qml` + Logger.log("WidgetLoader", `Attempting to load widget from: ${widgetPath}`) + + // Try to load the widget directly from file + const component = Qt.createComponent(widgetPath) + if (component.status === Component.Ready) { + Logger.log("WidgetLoader", `Successfully created component for: ${widgetName}.qml`) + return component + } + + const errorMsg = `Failed to load ${widgetName}.qml widget, status: ${component.status}, error: ${component.errorString()}` + Logger.error("WidgetLoader", errorMsg) + return null + } + + // Initialize loading tracking + function initializeLoading(widgetList) { + totalWidgets = widgetList.length + loadedWidgets = 0 + failedWidgets = 0 + + if (totalWidgets > 0) { + Logger.log("WidgetLoader", `Attempting to load ${totalWidgets} widgets`) + } + } + + // Track widget loading success + function onWidgetLoaded(widgetName) { + loadedWidgets++ + widgetLoaded(widgetName) + + if (loadedWidgets + failedWidgets === totalWidgets) { + Logger.log("WidgetLoader", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) + loadingComplete(totalWidgets, loadedWidgets, failedWidgets) + } + } + + // Track widget loading failure + function onWidgetFailed(widgetName, error) { + failedWidgets++ + widgetFailed(widgetName, error) + + if (loadedWidgets + failedWidgets === totalWidgets) { + Logger.log("WidgetLoader", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) + loadingComplete(totalWidgets, loadedWidgets, failedWidgets) + } + } +} diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 462b8c6..3e96aa0 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -23,9 +23,7 @@ Variants { implicitHeight: Style.barHeight * scaling color: Color.transparent - Component.onCompleted: { - logWidgetLoadingSummary() - } + // If no bar activated in settings, then show them all visible: modelData ? (Settings.data.bar.monitors.includes(modelData.name) @@ -64,16 +62,15 @@ Variants { Repeater { model: Settings.data.bar.widgets.left delegate: Loader { - id: widgetLoader - sourceComponent: getWidgetComponent(modelData) + id: leftWidgetLoader + sourceComponent: widgetLoader.getWidgetComponent(modelData) active: true anchors.verticalCenter: parent.verticalCenter onStatusChanged: { if (status === Loader.Error) { - Logger.error("Bar", `Failed to load ${modelData} widget`) - onWidgetFailed() + widgetLoader.onWidgetFailed(modelData, "Loader error") } else if (status === Loader.Ready) { - onWidgetLoaded() + widgetLoader.onWidgetLoaded(modelData) } } } @@ -92,16 +89,15 @@ Variants { Repeater { model: Settings.data.bar.widgets.center delegate: Loader { - id: widgetLoader - sourceComponent: getWidgetComponent(modelData) + id: centerWidgetLoader + sourceComponent: widgetLoader.getWidgetComponent(modelData) active: true anchors.verticalCenter: parent.verticalCenter onStatusChanged: { if (status === Loader.Error) { - Logger.error("Bar", `Failed to load ${modelData} widget`) - onWidgetFailed() + widgetLoader.onWidgetFailed(modelData, "Loader error") } else if (status === Loader.Ready) { - onWidgetLoaded() + widgetLoader.onWidgetLoaded(modelData) } } } @@ -121,16 +117,15 @@ Variants { Repeater { model: Settings.data.bar.widgets.right delegate: Loader { - id: widgetLoader - sourceComponent: getWidgetComponent(modelData) + id: rightWidgetLoader + sourceComponent: widgetLoader.getWidgetComponent(modelData) active: true anchors.verticalCenter: parent.verticalCenter onStatusChanged: { if (status === Loader.Error) { - Logger.error("Bar", `Failed to load ${modelData} widget`) - onWidgetFailed() + widgetLoader.onWidgetFailed(modelData, "Loader error") } else if (status === Loader.Ready) { - onWidgetLoaded() + widgetLoader.onWidgetLoaded(modelData) } } } @@ -138,60 +133,31 @@ Variants { } } - // Auto-discover widget components - function getWidgetComponent(widgetName) { - if (!widgetName || widgetName.trim() === "") { - return null + // Widget loader instance + WidgetLoader { + id: widgetLoader + + onWidgetLoaded: function(widgetName) { + Logger.log("Bar", `Widget loaded: ${widgetName}`) } - const widgetPath = `../Bar/Widgets/${widgetName}.qml` - Logger.log("Bar", `Attempting to load widget from: ${widgetPath}`) - - // Try to load the widget directly from file - const component = Qt.createComponent(widgetPath) - if (component.status === Component.Ready) { - Logger.log("Bar", `Successfully created component for: ${widgetName}.qml`) - return component + onWidgetFailed: function(widgetName, error) { + Logger.error("Bar", `Widget failed: ${widgetName} - ${error}`) } - Logger.error("Bar", `Failed to load ${widgetName}.qml widget, status: ${component.status}, error: ${component.errorString()}`) - return null + onLoadingComplete: function(total, loaded, failed) { + Logger.log("Bar", `Widget loading complete: ${loaded}/${total} loaded, ${failed} failed`) + } } - // Track widget loading status - property int totalWidgets: 0 - property int loadedWidgets: 0 - property int failedWidgets: 0 - - // Log widget loading summary - function logWidgetLoadingSummary() { + // Initialize widget loading tracking + Component.onCompleted: { const allWidgets = [ ...Settings.data.bar.widgets.left, ...Settings.data.bar.widgets.center, ...Settings.data.bar.widgets.right ] - - totalWidgets = allWidgets.length - loadedWidgets = 0 - failedWidgets = 0 - - if (totalWidgets > 0) { - Logger.log("Bar", `Attempting to load ${totalWidgets} widgets`) - } - } - - function onWidgetLoaded() { - loadedWidgets++ - if (loadedWidgets + failedWidgets === totalWidgets) { - Logger.log("Bar", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) - } - } - - function onWidgetFailed() { - failedWidgets++ - if (loadedWidgets + failedWidgets === totalWidgets) { - Logger.log("Bar", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) - } + widgetLoader.initializeLoading(allWidgets) } From 566e3e2aa79daec6f396c3e9bea2764cb9517089 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 19:27:16 +0200 Subject: [PATCH 08/10] Move more things to WidgetLoader.qml --- Commons/WidgetLoader.qml | 37 ++++++-- Modules/Bar/Bar.qml | 8 -- Modules/SettingsPanel/Tabs/BarTab.qml | 126 +++----------------------- Widgets/NComboBox.qml | 2 + 4 files changed, 46 insertions(+), 127 deletions(-) diff --git a/Commons/WidgetLoader.qml b/Commons/WidgetLoader.qml index fda9635..635fe04 100644 --- a/Commons/WidgetLoader.qml +++ b/Commons/WidgetLoader.qml @@ -21,12 +21,10 @@ QtObject { } const widgetPath = `../Modules/Bar/Widgets/${widgetName}.qml` - Logger.log("WidgetLoader", `Attempting to load widget from: ${widgetPath}`) // Try to load the widget directly from file const component = Qt.createComponent(widgetPath) if (component.status === Component.Ready) { - Logger.log("WidgetLoader", `Successfully created component for: ${widgetName}.qml`) return component } @@ -40,10 +38,6 @@ QtObject { totalWidgets = widgetList.length loadedWidgets = 0 failedWidgets = 0 - - if (totalWidgets > 0) { - Logger.log("WidgetLoader", `Attempting to load ${totalWidgets} widgets`) - } } // Track widget loading success @@ -52,7 +46,7 @@ QtObject { widgetLoaded(widgetName) if (loadedWidgets + failedWidgets === totalWidgets) { - Logger.log("WidgetLoader", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) + Logger.log("WidgetLoader", `Loaded ${loadedWidgets} widgets`) loadingComplete(totalWidgets, loadedWidgets, failedWidgets) } } @@ -63,8 +57,35 @@ QtObject { widgetFailed(widgetName, error) if (loadedWidgets + failedWidgets === totalWidgets) { - Logger.log("WidgetLoader", `Loaded ${loadedWidgets}/${totalWidgets} widgets`) loadingComplete(totalWidgets, loadedWidgets, failedWidgets) } } + + // This is where you should add your Modules/Bar/Widgets/ + // so it gets registered in the BarTab + function discoverAvailableWidgets() { + const widgetFiles = [ + "ActiveWindow", "Battery", "Bluetooth", "Brightness", "Clock", + "MediaMini", "NotificationHistory", "ScreenRecorderIndicator", + "SidePanelToggle", "SystemMonitor", "Tray", "Volume", "WiFi", "Workspace" + ] + + const availableWidgets = [] + + widgetFiles.forEach(widgetName => { + // Test if the widget can be loaded + const component = getWidgetComponent(widgetName) + if (component) { + availableWidgets.push({ + key: widgetName, + name: widgetName + }) + } + }) + + // Sort alphabetically + availableWidgets.sort((a, b) => a.name.localeCompare(b.name)) + + return availableWidgets + } } diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 3e96aa0..7e23d0e 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -137,17 +137,9 @@ Variants { WidgetLoader { id: widgetLoader - onWidgetLoaded: function(widgetName) { - Logger.log("Bar", `Widget loaded: ${widgetName}`) - } - onWidgetFailed: function(widgetName, error) { Logger.error("Bar", `Widget failed: ${widgetName} - ${error}`) } - - onLoadingComplete: function(total, loaded, failed) { - Logger.log("Bar", `Widget loading complete: ${loaded}/${total} loaded, ${failed} failed`) - } } // Initialize widget loading tracking diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index 525acb7..ebb9f2f 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -1,7 +1,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import Qt.labs.folderlistmodel import qs.Commons import qs.Services import qs.Widgets @@ -196,6 +195,7 @@ ColumnLayout { Item { Layout.fillWidth: true } NComboBox { + id: leftComboBox width: 120 * scaling model: availableWidgets label: "" @@ -319,6 +319,7 @@ ColumnLayout { Item { Layout.fillWidth: true } NComboBox { + id: centerComboBox width: 120 * scaling model: availableWidgets label: "" @@ -326,6 +327,7 @@ ColumnLayout { placeholder: "Add widget to center section" onSelected: key => { addWidgetToSection(key, "center") + reset() // Reset selection } } } @@ -442,6 +444,7 @@ ColumnLayout { Item { Layout.fillWidth: true } NComboBox { + id: rightComboBox width: 120 * scaling model: availableWidgets label: "" @@ -449,6 +452,7 @@ ColumnLayout { placeholder: "Add widget to right section" onSelected: key => { addWidgetToSection(key, "right") + reset() // Reset selection } } } @@ -585,13 +589,9 @@ ColumnLayout { } } - // Dynamic widget discovery using FolderListModel - FolderListModel { - id: widgetFolderModel - folder: Qt.resolvedUrl("../../Bar/Widgets/") - nameFilters: ["*.qml"] - showDirs: false - showFiles: true + // Widget loader for discovering available widgets + WidgetLoader { + id: widgetLoader } ListModel { @@ -602,115 +602,19 @@ ColumnLayout { discoverWidgets() } - // Automatically discover available widgets from the Widgets directory + // Automatically discover available widgets using WidgetLoader function discoverWidgets() { - console.log("Discovering widgets...") - console.log("FolderListModel count:", widgetFolderModel.count) - console.log("FolderListModel folder:", widgetFolderModel.folder) - availableWidgets.clear() - // Process each .qml file found in the directory - for (let i = 0; i < widgetFolderModel.count; i++) { - const fileName = widgetFolderModel.get(i, "fileName") - console.log("Found file:", fileName) - const widgetName = fileName.replace('.qml', '') - - // Skip TrayMenu as it's not a standalone widget - if (widgetName !== 'TrayMenu') { - console.log("Adding widget:", widgetName) - availableWidgets.append({ - key: widgetName, - name: widgetName, - icon: getDefaultIcon(widgetName) - }) - } - } + // Use WidgetLoader to discover available widgets + const discoveredWidgets = widgetLoader.discoverAvailableWidgets() - console.log("Total widgets added:", availableWidgets.count) - - // If FolderListModel didn't find anything, use fallback - if (availableWidgets.count === 0) { - console.log("FolderListModel failed, using fallback list") - const fallbackWidgets = [ - "ActiveWindow", "Battery", "Bluetooth", "Brightness", "Clock", - "MediaMini", "NotificationHistory", "ScreenRecorderIndicator", - "SidePanelToggle", "SystemMonitor", "Tray", "Volume", "WiFi", "Workspace" - ] - - fallbackWidgets.forEach(widgetName => { - availableWidgets.append({ - key: widgetName, - name: widgetName, - icon: getDefaultIcon(widgetName) - }) - }) - } - - // Sort alphabetically by name - sortWidgets() - } - - // Sort widgets alphabetically - function sortWidgets() { - const widgets = [] - for (let i = 0; i < availableWidgets.count; i++) { - widgets.push({ - key: availableWidgets.get(i).key, - name: availableWidgets.get(i).name, - icon: availableWidgets.get(i).icon - }) - } - - widgets.sort((a, b) => a.name.localeCompare(b.name)) - - availableWidgets.clear() - widgets.forEach(widget => { + // Add discovered widgets to the ListModel + discoveredWidgets.forEach(widget => { availableWidgets.append(widget) }) - } - - // Get default icon for widget (can be overridden in widget files) - function getDefaultIcon(widgetName) { - const iconMap = { - "ActiveWindow": "web_asset", - "Battery": "battery_full", - "Bluetooth": "bluetooth", - "Brightness": "brightness_6", - "Clock": "schedule", - "MediaMini": "music_note", - "NotificationHistory": "notifications", - "ScreenRecorderIndicator": "videocam", - "SidePanelToggle": "widgets", - "SystemMonitor": "memory", - "Tray": "apps", - "Volume": "volume_up", - "WiFi": "wifi", - "Workspace": "dashboard" - } - return iconMap[widgetName] || "widgets" + + } - - - // Helper function to get widget icons - function getWidgetIcon(widgetKey) { - switch(widgetKey) { - case "SystemMonitor": return "memory" - case "ActiveWindow": return "web_asset" - case "MediaMini": return "music_note" - case "Workspace": return "dashboard" - case "ScreenRecorderIndicator": return "videocam" - case "Tray": return "apps" - case "NotificationHistory": return "notifications" - case "WiFi": return "wifi" - case "Bluetooth": return "bluetooth" - case "Battery": return "battery_full" - case "Volume": return "volume_up" - case "Brightness": return "brightness_6" - case "Clock": return "schedule" - case "SidePanelToggle": return "widgets" - default: return "widgets" - } - } } diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml index c82abb0..21dc986 100644 --- a/Widgets/NComboBox.qml +++ b/Widgets/NComboBox.qml @@ -37,6 +37,8 @@ ColumnLayout { return -1 } + + ComboBox { id: combo Layout.fillWidth: true From ce9ab7f90f166e4966bf94d8ee0e73d6e4a1f9f6 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 19:57:29 +0200 Subject: [PATCH 09/10] Formatting --- Commons/Color.qml | 3 +- Commons/Settings.qml | 2 +- Commons/WidgetLoader.qml | 45 +++++++-------- Modules/Bar/Bar.qml | 16 +----- Modules/Bar/Widgets/Bluetooth.qml | 1 - Modules/Bar/Widgets/Tray.qml | 3 - Modules/SettingsPanel/Tabs/BarTab.qml | 80 ++++++++++++++------------- Widgets/NComboBox.qml | 9 ++- 8 files changed, 73 insertions(+), 86 deletions(-) diff --git a/Commons/Color.qml b/Commons/Color.qml index f31f1ac..dc1d554 100644 --- a/Commons/Color.qml +++ b/Commons/Color.qml @@ -47,7 +47,8 @@ Singleton { // ----------- function applyOpacity(color, opacity) { // Convert color to string and apply opacity - if (!color) return "transparent" + if (!color) + return "transparent" return color.toString().replace("#", "#" + opacity) } diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 0ffe01c..77aaaaf 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -133,7 +133,7 @@ Singleton { // Widget configuration for modular bar system property JsonObject widgets - + widgets: JsonObject { property list left: ["SystemMonitor", "ActiveWindow", "MediaMini"] property list center: ["Workspace"] diff --git a/Commons/WidgetLoader.qml b/Commons/WidgetLoader.qml index 635fe04..ce44431 100644 --- a/Commons/WidgetLoader.qml +++ b/Commons/WidgetLoader.qml @@ -19,16 +19,17 @@ QtObject { if (!widgetName || widgetName.trim() === "") { return null } - + const widgetPath = `../Modules/Bar/Widgets/${widgetName}.qml` - + // Try to load the widget directly from file const component = Qt.createComponent(widgetPath) if (component.status === Component.Ready) { return component } - - const errorMsg = `Failed to load ${widgetName}.qml widget, status: ${component.status}, error: ${component.errorString()}` + + const errorMsg = `Failed to load ${widgetName}.qml widget, status: ${component.status}, error: ${component.errorString( + )}` Logger.error("WidgetLoader", errorMsg) return null } @@ -44,7 +45,7 @@ QtObject { function onWidgetLoaded(widgetName) { loadedWidgets++ widgetLoaded(widgetName) - + if (loadedWidgets + failedWidgets === totalWidgets) { Logger.log("WidgetLoader", `Loaded ${loadedWidgets} widgets`) loadingComplete(totalWidgets, loadedWidgets, failedWidgets) @@ -55,7 +56,7 @@ QtObject { function onWidgetFailed(widgetName, error) { failedWidgets++ widgetFailed(widgetName, error) - + if (loadedWidgets + failedWidgets === totalWidgets) { loadingComplete(totalWidgets, loadedWidgets, failedWidgets) } @@ -64,28 +65,24 @@ QtObject { // This is where you should add your Modules/Bar/Widgets/ // so it gets registered in the BarTab function discoverAvailableWidgets() { - const widgetFiles = [ - "ActiveWindow", "Battery", "Bluetooth", "Brightness", "Clock", - "MediaMini", "NotificationHistory", "ScreenRecorderIndicator", - "SidePanelToggle", "SystemMonitor", "Tray", "Volume", "WiFi", "Workspace" - ] - + const widgetFiles = ["ActiveWindow", "Battery", "Bluetooth", "Brightness", "Clock", "MediaMini", "NotificationHistory", "ScreenRecorderIndicator", "SidePanelToggle", "SystemMonitor", "Tray", "Volume", "WiFi", "Workspace"] + const availableWidgets = [] - + widgetFiles.forEach(widgetName => { - // Test if the widget can be loaded - const component = getWidgetComponent(widgetName) - if (component) { - availableWidgets.push({ - key: widgetName, - name: widgetName - }) - } - }) - + // Test if the widget can be loaded + const component = getWidgetComponent(widgetName) + if (component) { + availableWidgets.push({ + "key": widgetName, + "name": widgetName + }) + } + }) + // Sort alphabetically availableWidgets.sort((a, b) => a.name.localeCompare(b.name)) - + return availableWidgets } } diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 7e23d0e..ae0ad9f 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -23,8 +23,6 @@ Variants { implicitHeight: Style.barHeight * scaling color: Color.transparent - - // If no bar activated in settings, then show them all visible: modelData ? (Settings.data.bar.monitors.includes(modelData.name) || (Settings.data.bar.monitors.length === 0)) : false @@ -136,24 +134,16 @@ Variants { // Widget loader instance WidgetLoader { id: widgetLoader - - onWidgetFailed: function(widgetName, error) { + + onWidgetFailed: function (widgetName, error) { Logger.error("Bar", `Widget failed: ${widgetName} - ${error}`) } } // Initialize widget loading tracking Component.onCompleted: { - const allWidgets = [ - ...Settings.data.bar.widgets.left, - ...Settings.data.bar.widgets.center, - ...Settings.data.bar.widgets.right - ] + const allWidgets = [...Settings.data.bar.widgets.left, ...Settings.data.bar.widgets.center, ...Settings.data.bar.widgets.right] widgetLoader.initializeLoading(allWidgets) } - - - - } } diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index 35ff4d5..bbe35f1 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -7,7 +7,6 @@ import qs.Commons import qs.Services import qs.Widgets - NIconButton { id: root diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 9998fe6..d85a174 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -9,13 +9,10 @@ import qs.Commons import qs.Services import qs.Widgets - - Rectangle { readonly property real itemSize: 24 * scaling visible: Settings.data.bar.showTray && (SystemTray.items.values.length > 0) - width: tray.width + Style.marginM * scaling * 2 diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index ebb9f2f..c751dcb 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -166,13 +166,14 @@ ColumnLayout { Layout.fillWidth: true Layout.minimumHeight: { var widgetCount = Settings.data.bar.widgets.left.length - if (widgetCount === 0) return 140 * scaling - + if (widgetCount === 0) + return 140 * scaling + var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) var rows = Math.ceil(widgetCount / widgetsPerRow) - + // Header (50) + spacing (20) + (rows * widget height) + (rows-1 * spacing) + bottom margin (20) return (50 + 20 + (rows * 48) + ((rows - 1) * Style.marginS) + 20) * scaling } @@ -192,7 +193,9 @@ ColumnLayout { color: Color.mOnSurface } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } NComboBox { id: leftComboBox @@ -202,8 +205,8 @@ ColumnLayout { description: "" placeholder: "Add widget to left section" onSelected: key => { - addWidgetToSection(key, "left") - } + addWidgetToSection(key, "left") + } } } @@ -290,13 +293,14 @@ ColumnLayout { Layout.fillWidth: true Layout.minimumHeight: { var widgetCount = Settings.data.bar.widgets.center.length - if (widgetCount === 0) return 140 * scaling - + if (widgetCount === 0) + return 140 * scaling + var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) var rows = Math.ceil(widgetCount / widgetsPerRow) - + // Header (50) + spacing (20) + (rows * widget height) + (rows-1 * spacing) + bottom margin (20) return (50 + 20 + (rows * 48) + ((rows - 1) * Style.marginS) + 20) * scaling } @@ -316,7 +320,9 @@ ColumnLayout { color: Color.mOnSurface } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } NComboBox { id: centerComboBox @@ -326,9 +332,9 @@ ColumnLayout { description: "" placeholder: "Add widget to center section" onSelected: key => { - addWidgetToSection(key, "center") - reset() // Reset selection - } + addWidgetToSection(key, "center") + reset() // Reset selection + } } } @@ -415,13 +421,14 @@ ColumnLayout { Layout.fillWidth: true Layout.minimumHeight: { var widgetCount = Settings.data.bar.widgets.right.length - if (widgetCount === 0) return 140 * scaling - + if (widgetCount === 0) + return 140 * scaling + var availableWidth = scrollView.availableWidth - (Style.marginM * scaling * 2) // Card margins var avgWidgetWidth = 150 * scaling // Estimated widget width including spacing var widgetsPerRow = Math.max(1, Math.floor(availableWidth / avgWidgetWidth)) var rows = Math.ceil(widgetCount / widgetsPerRow) - + // Header (50) + spacing (20) + (rows * widget height) + (rows-1 * spacing) + bottom margin (20) return (50 + 20 + (rows * 48) + ((rows - 1) * Style.marginS) + 20) * scaling } @@ -441,7 +448,9 @@ ColumnLayout { color: Color.mOnSurface } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } NComboBox { id: rightComboBox @@ -451,9 +460,9 @@ ColumnLayout { description: "" placeholder: "Add widget to right section" onSelected: key => { - addWidgetToSection(key, "right") - reset() // Reset selection - } + addWidgetToSection(key, "right") + reset() // Reset selection + } } } @@ -540,8 +549,6 @@ ColumnLayout { } } - - // Helper functions function addWidgetToSection(widgetName, section) { console.log("Adding widget", widgetName, "to section", section) @@ -551,7 +558,7 @@ ColumnLayout { var newArray = sectionArray.slice() newArray.push(widgetName) console.log("Widget added. New array:", JSON.stringify(newArray)) - + // Assign the new array Settings.data.bar.widgets[section] = newArray } @@ -565,7 +572,7 @@ ColumnLayout { var newArray = sectionArray.slice() newArray.splice(index, 1) console.log("Widget removed. New array:", JSON.stringify(newArray)) - + // Assign the new array Settings.data.bar.widgets[section] = newArray } @@ -574,16 +581,16 @@ ColumnLayout { function reorderWidgetInSection(section, fromIndex, toIndex) { console.log("Reordering widget in section", section, "from", fromIndex, "to", toIndex) var sectionArray = Settings.data.bar.widgets[section] - if (sectionArray && fromIndex >= 0 && fromIndex < sectionArray.length && - toIndex >= 0 && toIndex < sectionArray.length) { - + if (sectionArray && fromIndex >= 0 && fromIndex < sectionArray.length && toIndex >= 0 + && toIndex < sectionArray.length) { + // Create a new array to avoid modifying the original var newArray = sectionArray.slice() var item = newArray[fromIndex] newArray.splice(fromIndex, 1) newArray.splice(toIndex, 0, item) console.log("Widget reordered. New array:", JSON.stringify(newArray)) - + // Assign the new array Settings.data.bar.widgets[section] = newArray } @@ -593,28 +600,25 @@ ColumnLayout { WidgetLoader { id: widgetLoader } - + ListModel { id: availableWidgets } - + Component.onCompleted: { discoverWidgets() } - + // Automatically discover available widgets using WidgetLoader function discoverWidgets() { availableWidgets.clear() - + // Use WidgetLoader to discover available widgets const discoveredWidgets = widgetLoader.discoverAvailableWidgets() - + // Add discovered widgets to the ListModel discoveredWidgets.forEach(widget => { - availableWidgets.append(widget) - }) - - + availableWidgets.append(widget) + }) } - } diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml index 21dc986..1cb33bb 100644 --- a/Widgets/NComboBox.qml +++ b/Widgets/NComboBox.qml @@ -37,8 +37,6 @@ ColumnLayout { return -1 } - - ComboBox { id: combo Layout.fillWidth: true @@ -64,9 +62,10 @@ ColumnLayout { font.pointSize: Style.fontSizeM * scaling verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - color: (combo.currentIndex >= 0 && combo.currentIndex < root.model.count) ? Color.mOnSurface : Color.mOnSurfaceVariant - text: (combo.currentIndex >= 0 && combo.currentIndex < root.model.count) ? root.model.get( - combo.currentIndex).name : root.placeholder + color: (combo.currentIndex >= 0 + && combo.currentIndex < root.model.count) ? Color.mOnSurface : Color.mOnSurfaceVariant + text: (combo.currentIndex >= 0 + && combo.currentIndex < root.model.count) ? root.model.get(combo.currentIndex).name : root.placeholder } indicator: NIcon { From 931e499d67c8f6ff4c849d13582206acc980cea5 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 22 Aug 2025 20:07:06 +0200 Subject: [PATCH 10/10] Remove some Settings entries --- Commons/Settings.qml | 7 +------ Modules/Bar/Widgets/MediaMini.qml | 3 ++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 77aaaaf..dcb664a 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -120,13 +120,8 @@ Singleton { bar: JsonObject { property string position: "top" // Possible values: "top", "bottom" - property bool showActiveWindow: true property bool showActiveWindowIcon: true - property bool showSystemInfo: false - property bool showMedia: false - property bool showBrightness: true - property bool showNotificationsHistory: true - property bool showTray: true + property bool alwaysShowBatteryPercentage: false property real backgroundOpacity: 1.0 property list monitors: [] diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml index a061031..7f51917 100644 --- a/Modules/Bar/Widgets/MediaMini.qml +++ b/Modules/Bar/Widgets/MediaMini.qml @@ -11,7 +11,8 @@ Row { id: root anchors.verticalCenter: parent.verticalCenter spacing: Style.marginS * scaling - visible: Settings.data.bar.showMedia && (MediaService.canPlay || MediaService.canPause) + visible: MediaService.currentPlayer !== null + width: MediaService.currentPlayer !== null ? implicitWidth : 0 function getTitle() { return MediaService.trackTitle + (MediaService.trackArtist !== "" ? ` - ${MediaService.trackArtist}` : "")