From 440f70a5c90cdf9eef9d6fd864491beda226f374 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 7 Nov 2025 21:48:07 +0100 Subject: [PATCH 01/21] NotificationHistoryPanel: fix misaligned icon --- .../NotificationHistoryPanel.qml | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml b/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml index 04df5cb4..dd1c87a8 100644 --- a/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml +++ b/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml @@ -200,22 +200,16 @@ SmartPanel { width: parent.width spacing: Style.marginM - // Icon column - use simple Item instead of ColumnLayout to avoid polish loop - Item { + // Icon - properly centered vertically + NImageCircled { Layout.preferredWidth: Math.round(40 * Style.uiScaleRatio) - Layout.alignment: Qt.AlignTop - - NImageCircled { - anchors.top: parent.top - anchors.topMargin: 20 - width: Math.round(40 * Style.uiScaleRatio) - height: Math.round(40 * Style.uiScaleRatio) - imagePath: model.cachedImage || model.originalImage || "" - borderColor: Color.transparent - borderWidth: 0 - fallbackIcon: "bell" - fallbackIconSize: 24 - } + Layout.preferredHeight: Math.round(40 * Style.uiScaleRatio) + Layout.alignment: Qt.AlignVCenter + imagePath: model.cachedImage || model.originalImage || "" + borderColor: Color.transparent + borderWidth: 0 + fallbackIcon: "bell" + fallbackIconSize: 24 } // Notification content column From 6290340988411968b46644a56ffb9597b57caf28 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 15:58:06 -0500 Subject: [PATCH 02/21] Noctalia Performance Mode: basic backend and toast. - Could use a bar widget - Could use a control center widget --- Assets/Translations/de.json | 13 +++++++++---- Assets/Translations/en.json | 13 +++++++++---- Assets/Translations/es.json | 13 +++++++++---- Assets/Translations/fr.json | 13 +++++++++---- Assets/Translations/pt.json | 13 +++++++++---- Assets/Translations/tr.json | 13 +++++++++---- Assets/Translations/uk-UA.json | 13 +++++++++---- Assets/Translations/zh-CN.json | 13 +++++++++---- Commons/Style.qml | 14 ++++++++------ Services/IPCService.qml | 14 +++++++++++++- Services/NotificationService.qml | 2 +- Services/PowerProfileService.qml | 23 +++++++++++++++++++++++ Widgets/NDropShadows.qml | 3 ++- 13 files changed, 119 insertions(+), 41 deletions(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 255b118a..f82cef4e 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -1511,10 +1511,6 @@ "description": "Die Paneele rasten an der Stange und den Bildschirmrändern ein und sorgen so für ein nahtloses Erscheinungsbild mit stilvollen, umgekehrten Ecken.", "label": "Paneele an Kanten ausrichten" }, - "settings-panel-attached-to-bar": { - "description": "Richten Sie das Einstellungsfenster an der Leiste aus, um ein einheitliches Erscheinungsbild zu erhalten.", - "label": "Einstellungsfenster an Leiste ausrichten" - }, "panels-overlay": { "description": "Stellt sicher, dass Panels und die Leiste sichtbar bleiben, auch über Vollbildanwendungen.", "label": "Panels & Leiste oben behalten" @@ -1528,6 +1524,10 @@ "description": "Passen Sie das Aussehen, die Haptik und das Verhalten der Benutzeroberfläche an.", "label": "Aussehen" }, + "settings-panel-attached-to-bar": { + "description": "Richten Sie das Einstellungsfenster an der Leiste aus, um ein einheitliches Erscheinungsbild zu erhalten.", + "label": "Einstellungsfenster an Leiste ausrichten" + }, "shadows": { "description": "Aktiviert Schlagschatten unter Balken und Panels.", "direction": { @@ -1734,6 +1734,11 @@ "normal": "Normaler Modus", "not-installed": "wlsunset nicht installiert" }, + "noctalia-performance": { + "disabled": "Leistungsmodus deaktiviert", + "enabled": "Leistungsmodus aktiviert", + "label": "Noctalia-Aufführung" + }, "power-profile": { "changed": "Energieprofil geändert", "profile-name": "\"{profile}\"" diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index a8ad2fe6..ff6b949f 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -1511,10 +1511,6 @@ "description": "Panels lock to the bar and screen edges, creating a seamless look with stylish inverted corners.", "label": "Snap panels to edges" }, - "settings-panel-attached-to-bar": { - "description": "Keep the Settings window aligned with the bar for a unified look.", - "label": "Snap Settings window to bar" - }, "panels-overlay": { "description": "Ensures panels and the bar remain visible, even over fullscreen applications.", "label": "Keep panels & bar on top" @@ -1528,6 +1524,10 @@ "description": "Customize the look, feel, and behavior of the interface.", "label": "Appearance" }, + "settings-panel-attached-to-bar": { + "description": "Keep the Settings window aligned with the bar for a unified look.", + "label": "Snap Settings window to bar" + }, "shadows": { "description": "Enables drop shadows under bars and panels.", "direction": { @@ -1734,6 +1734,11 @@ "normal": "Normal mode", "not-installed": "wlsunset not installed" }, + "noctalia-performance": { + "disabled": "Performance mode disabled", + "enabled": "Performance mode enabled", + "label": "Noctalia performance" + }, "power-profile": { "changed": "Power profile changed", "profile-name": "\"{profile}\"" diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 6fc5b1e6..147d9b3f 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -1511,10 +1511,6 @@ "description": "Los paneles se fijan a la barra y a los bordes de la pantalla, creando una apariencia impecable con elegantes esquinas invertidas.", "label": "Ajustar paneles a los bordes" }, - "settings-panel-attached-to-bar": { - "description": "Mantén la ventana de Configuración alineada con la barra para un aspecto unificado.", - "label": "Ajustar Configuración a la barra" - }, "panels-overlay": { "description": "Garantiza que los paneles y la barra permanezcan visibles, incluso sobre aplicaciones en pantalla completa.", "label": "Mantener paneles y barra en la parte superior" @@ -1528,6 +1524,10 @@ "description": "Personaliza la apariencia, el ambiente y el comportamiento de la interfaz.", "label": "Apariencia" }, + "settings-panel-attached-to-bar": { + "description": "Mantén la ventana de Configuración alineada con la barra para un aspecto unificado.", + "label": "Ajustar Configuración a la barra" + }, "shadows": { "description": "Habilita sombras paralelas debajo de las barras y los paneles.", "direction": { @@ -1734,6 +1734,11 @@ "normal": "Modo normal", "not-installed": "wlsunset no está instalado" }, + "noctalia-performance": { + "disabled": "Modo de rendimiento desactivado", + "enabled": "Modo de rendimiento activado", + "label": "Actuación de Noctalia" + }, "power-profile": { "changed": "Perfil de energía cambiado", "profile-name": "\"{profile}\"" diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index e4d32147..8409bd0f 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -1511,10 +1511,6 @@ "description": "Les panneaux se verrouillent sur la barre et les bords de l'écran, créant un aspect homogène avec d'élégants coins inversés.", "label": "Aligner les panneaux aux bords" }, - "settings-panel-attached-to-bar": { - "description": "Alignez la fenêtre des paramètres sur la barre pour un rendu cohérent.", - "label": "Aligner la fenêtre des paramètres sur la barre" - }, "panels-overlay": { "description": "Garantit que les panneaux et la barre restent visibles, même au-dessus des applications en plein écran.", "label": "Garder les panneaux et la barre en haut." @@ -1528,6 +1524,10 @@ "description": "Personnaliser l'apparence, l'ergonomie et le comportement de l'interface.", "label": "Apparence" }, + "settings-panel-attached-to-bar": { + "description": "Alignez la fenêtre des paramètres sur la barre pour un rendu cohérent.", + "label": "Aligner la fenêtre des paramètres sur la barre" + }, "shadows": { "description": "Active les ombres portées sous les barres et les panneaux.", "direction": { @@ -1734,6 +1734,11 @@ "normal": "Mode normal", "not-installed": "wlsunset non installé" }, + "noctalia-performance": { + "disabled": "Mode performance désactivé", + "enabled": "Mode performance activé", + "label": "Performance de Noctalia" + }, "power-profile": { "changed": "Profil d'alimentation changé", "profile-name": "\"{profile}\"" diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 8d9876ae..dab199b6 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -1511,10 +1511,6 @@ "description": "Os painéis travam na barra e nas bordas da tela, criando uma aparência perfeita com cantos invertidos elegantes.", "label": "Ajustar Painéis às Bordas" }, - "settings-panel-attached-to-bar": { - "description": "Mantenha a janela de Configurações alinhada com a barra para um visual uniforme.", - "label": "Ajustar Configurações à barra" - }, "panels-overlay": { "description": "Garante que os painéis e a barra permaneçam visíveis, mesmo sobre aplicativos em tela cheia.", "label": "Manter Painéis e Barra no Topo" @@ -1528,6 +1524,10 @@ "description": "Personalize a aparência, a sensação e o comportamento da interface.", "label": "Aparência" }, + "settings-panel-attached-to-bar": { + "description": "Mantenha a janela de Configurações alinhada com a barra para um visual uniforme.", + "label": "Ajustar Configurações à barra" + }, "shadows": { "description": "Ativa sombras projetadas sob barras e painéis.", "direction": { @@ -1734,6 +1734,11 @@ "normal": "Modo normal", "not-installed": "wlsunset não está instalado" }, + "noctalia-performance": { + "disabled": "Modo de desempenho desativado", + "enabled": "Modo de desempenho ativado", + "label": "Apresentação de Noctalia" + }, "power-profile": { "changed": "Perfil de energia alterado", "profile-name": "\"{profile}\"" diff --git a/Assets/Translations/tr.json b/Assets/Translations/tr.json index f01e0438..749063e9 100644 --- a/Assets/Translations/tr.json +++ b/Assets/Translations/tr.json @@ -1511,10 +1511,6 @@ "description": "Paneller çubuğa ve ekran kenarlarına kilitlenerek şık ters köşelerle kusursuz bir görünüm oluşturur.", "label": "Kenarları Yakala" }, - "settings-panel-attached-to-bar": { - "description": "Ayarlar penceresini çubukla hizalayarak tutarlı bir görünüm sağlayın.", - "label": "Ayarlar penceresini çubuğa hizala" - }, "panels-overlay": { "description": "Panellerin ve çubuğun, tam ekran uygulamalarda bile görünür kalmasını sağlar.", "label": "Panelleri ve Çubuğu Üstte Tut" @@ -1528,6 +1524,10 @@ "description": "Arayüzün görünümünü, hissini ve davranışını özelleştirin.", "label": "Görünüm" }, + "settings-panel-attached-to-bar": { + "description": "Ayarlar penceresini çubukla hizalayarak tutarlı bir görünüm sağlayın.", + "label": "Ayarlar penceresini çubuğa hizala" + }, "shadows": { "description": "Çubukların ve panellerin altında gölgelerin etkinleştirilmesini sağlar.", "direction": { @@ -1734,6 +1734,11 @@ "normal": "Normal mod", "not-installed": "wlsunset kurulu değil" }, + "noctalia-performance": { + "disabled": "Performans modu devre dışı bırakıldı", + "enabled": "Performans modu etkinleştirildi", + "label": "Noctalia performansı" + }, "power-profile": { "changed": "Güç profili değişti", "profile-name": "\"{profile}\"" diff --git a/Assets/Translations/uk-UA.json b/Assets/Translations/uk-UA.json index 71c2db8f..f32018c2 100644 --- a/Assets/Translations/uk-UA.json +++ b/Assets/Translations/uk-UA.json @@ -1511,10 +1511,6 @@ "description": "Панелі фіксуються до штанги та країв екрана, створюючи бездоганний вигляд зі стильними перевернутими кутами.", "label": "Прив'язувати панелі до країв" }, - "settings-panel-attached-to-bar": { - "description": "Вирівнюйте вікно налаштувань відносно панелі, щоб зберегти цілісний вигляд.", - "label": "Прив'язувати вікно налаштувань до панелі" - }, "panels-overlay": { "description": "Забезпечує видимість панелей і панелі завдань, навіть поверх повноекранних програм.", "label": "Тримати панелі та панель зверху" @@ -1528,6 +1524,10 @@ "description": "Налаштуйте вигляд, відчуття та поведінку інтерфейсу.", "label": "Зовнішній вигляд" }, + "settings-panel-attached-to-bar": { + "description": "Вирівнюйте вікно налаштувань відносно панелі, щоб зберегти цілісний вигляд.", + "label": "Прив'язувати вікно налаштувань до панелі" + }, "shadows": { "description": "Увімкнути тіні під панелями та смугами.", "direction": { @@ -1734,6 +1734,11 @@ "normal": "Звичайний режим", "not-installed": "wlsunset не встановлено" }, + "noctalia-performance": { + "disabled": "Режим продуктивності вимкнено.", + "enabled": "Режим продуктивності ввімкнено.", + "label": "Виступ Noctalia" + }, "power-profile": { "changed": "Профіль живлення змінено", "profile-name": "\"{profile}\"" diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 5f9a8026..4c6c57d6 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -1511,10 +1511,6 @@ "description": "面板锁定到边栏和屏幕边缘,形成无缝外观,并具有时尚的倒角。", "label": "将面板对齐到边缘" }, - "settings-panel-attached-to-bar": { - "description": "使设置窗口与边栏保持对齐,实现统一的外观。", - "label": "将设置窗口贴合边栏" - }, "panels-overlay": { "description": "确保面板和栏保持可见,即使在全屏应用程序上也是如此。", "label": "将面板和栏保持在顶部" @@ -1528,6 +1524,10 @@ "description": "自定义界面的外观、感觉和行为。", "label": "外观" }, + "settings-panel-attached-to-bar": { + "description": "使设置窗口与边栏保持对齐,实现统一的外观。", + "label": "将设置窗口贴合边栏" + }, "shadows": { "description": "启用条形图和面板下的阴影。", "direction": { @@ -1734,6 +1734,11 @@ "normal": "正常模式", "not-installed": "未安装 wlsunset" }, + "noctalia-performance": { + "disabled": "性能模式已禁用", + "enabled": "已启用性能模式", + "label": "夜之颂演出" + }, "power-profile": { "changed": "电源模式已更改", "profile-name": "\"{profile}\"" diff --git a/Commons/Style.qml b/Commons/Style.qml index d82f82ed..bb06ff05 100644 --- a/Commons/Style.qml +++ b/Commons/Style.qml @@ -4,13 +4,15 @@ import QtQuick import Quickshell import Quickshell.Io +import qs.Services + Singleton { id: root /* Preset sizes for font, radii, ? - */ + */ // Font size property real fontSizeXXS: 8 @@ -65,11 +67,11 @@ Singleton { property real shadowVerticalOffset: Settings.data.general.shadowOffsetY // Animation duration (ms) - property int animationFaster: Settings.data.general.animationDisabled ? 0 : Math.round(75 / Settings.data.general.animationSpeed) - property int animationFast: Settings.data.general.animationDisabled ? 0 : Math.round(150 / Settings.data.general.animationSpeed) - property int animationNormal: Settings.data.general.animationDisabled ? 0 : Math.round(300 / Settings.data.general.animationSpeed) - property int animationSlow: Settings.data.general.animationDisabled ? 0 : Math.round(450 / Settings.data.general.animationSpeed) - property int animationSlowest: Settings.data.general.animationDisabled ? 0 : Math.round(750 / Settings.data.general.animationSpeed) + property int animationFaster: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(75 / Settings.data.general.animationSpeed) + property int animationFast: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(150 / Settings.data.general.animationSpeed) + property int animationNormal: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(300 / Settings.data.general.animationSpeed) + property int animationSlow: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(450 / Settings.data.general.animationSpeed) + property int animationSlowest: (Settings.data.general.animationDisabled || PowerProfileService.noctaliaPerformanceMode) ? 0 : Math.round(750 / Settings.data.general.animationSpeed) // Delays property int tooltipDelay: 300 diff --git a/Services/IPCService.qml b/Services/IPCService.qml index 6788b02c..a6cac693 100644 --- a/Services/IPCService.qml +++ b/Services/IPCService.qml @@ -40,7 +40,7 @@ Item { function toggle() { root.withTargetScreen(screen => { var calendarPanel = PanelService.getPanel("calendarPanel", screen) - calendarPanel.toggle(null, "Clock") + calendarPanel?.toggle(null, "Clock") }) } } @@ -287,6 +287,18 @@ Item { break } } + + function toggleNoctaliaPerformance() { + PowerProfileService.toggleNoctaliaPerformance() + } + + function enableNoctaliaPerformance() { + PowerProfileService.setNoctaliaPerformance(true) + } + + function disableNoctaliaPerformance() { + PowerProfileService.setNoctaliaPerformance(false) + } } IpcHandler { diff --git a/Services/NotificationService.qml b/Services/NotificationService.qml index 5ef0a705..c943d875 100644 --- a/Services/NotificationService.qml +++ b/Services/NotificationService.qml @@ -123,7 +123,7 @@ Singleton { const data = createData(notification) addToHistory(data) - if (Settings.data.notifications?.doNotDisturb) + if (Settings.data.notifications?.doNotDisturb || PowerProfileService.noctaliaPerformanceMode) return activeMap[data.id] = notification diff --git a/Services/PowerProfileService.qml b/Services/PowerProfileService.qml index 2417b81b..160e5213 100644 --- a/Services/PowerProfileService.qml +++ b/Services/PowerProfileService.qml @@ -13,6 +13,9 @@ Singleton { readonly property bool available: powerProfiles && powerProfiles.hasPerformanceProfile property int profile: powerProfiles ? powerProfiles.profile : PowerProfile.Balanced + // Not a power profile but a volatile property to quickly disable shadows, animations, etc.. + property bool noctaliaPerformanceMode: false + function getName(p) { if (!available) return "Unknown" @@ -94,4 +97,24 @@ Singleton { } } } + + // Noctalia Performance Mode + // - Turning shadow off + // - Turning animation off + // - Do Not Disturb + function toggleNoctaliaPerformance() { + noctaliaPerformanceMode = !noctaliaPerformanceMode + } + + function setNoctaliaPerformance(value) { + noctaliaPerformanceMode = value + } + + onNoctaliaPerformanceModeChanged: { + if (noctaliaPerformanceMode) { + ToastService.showNotice(I18n.tr("toast.noctalia-performance.label"), I18n.tr("toast.noctalia-performance.enabled"), "seedling") + } else { + ToastService.showNotice(I18n.tr("toast.noctalia-performance.label"), I18n.tr("toast.noctalia-performance.disabled"), "seedling-off") + } + } } diff --git a/Widgets/NDropShadows.qml b/Widgets/NDropShadows.qml index 74342222..9dc2a4e5 100644 --- a/Widgets/NDropShadows.qml +++ b/Widgets/NDropShadows.qml @@ -1,13 +1,14 @@ import QtQuick import QtQuick.Effects import qs.Commons +import qs.Services // Unified shadow system Item { id: root required property var source - layer.enabled: Settings.data.general.enableShadows + layer.enabled: Settings.data.general.enableShadows && !PowerProfileService.noctaliaPerformanceMode layer.effect: MultiEffect { source: root.source shadowEnabled: true From b180325691dcf45c7df1574c0e3e6c62e051b510 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 16:11:12 -0500 Subject: [PATCH 03/21] SetupWizard: Improved kickstart and fixed odd size on tiny screens. --- Modules/Panels/SetupWizard/SetupWizard.qml | 4 +- shell.qml | 48 ++++++++++------------ 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/Modules/Panels/SetupWizard/SetupWizard.qml b/Modules/Panels/SetupWizard/SetupWizard.qml index 466f618d..74dd1fb3 100644 --- a/Modules/Panels/SetupWizard/SetupWizard.qml +++ b/Modules/Panels/SetupWizard/SetupWizard.qml @@ -11,8 +11,8 @@ import qs.Modules.MainScreen SmartPanel { id: root - preferredWidth: Math.round(520 * Style.uiScaleRatio) - preferredHeight: Math.round(600 * Style.uiScaleRatio) + preferredWidth: Math.round(preferredWidthRatio * 2560 * Style.uiScaleRatio) + preferredHeight: Math.round(preferredHeightRatio * 1440 * Style.uiScaleRatio) preferredWidthRatio: 0.4 preferredHeightRatio: 0.6 diff --git a/shell.qml b/shell.qml index 00910337..3f433162 100644 --- a/shell.qml +++ b/shell.qml @@ -74,6 +74,11 @@ ShellRoot { IdleInhibitorService.init() PowerProfileService.init() DistroService.init() + + // Only open the setup wizard for new users + if (!Settings.data.setupCompleted) { + checkSetupWizard() + } } Overview {} @@ -100,17 +105,6 @@ ShellRoot { } } - // Setup Wizard - Auto Kick start - Connections { - target: Settings - function onSettingsLoaded() { - // Only open the setup wizard for new users - if (!Settings.data.setupCompleted) { - checkSetupWizard() - } - } - } - function checkSetupWizard() { // Wait for distro service if (!DistroService.isReady) { @@ -125,23 +119,25 @@ ShellRoot { } if (Settings.data.settingsVersion >= Settings.settingsVersion) { - // Open Setup Wizard as a panel in the same windowing system as Settings/ControlCenter - if (Quickshell.screens.length > 0) { - var targetScreen = Quickshell.screens[0] - var setupPanel = PanelService.getPanel("setupWizardPanel", targetScreen) - if (setupPanel) { - setupPanel.open() - } else { - // If not yet loaded, ensure it loads and try again shortly - Qt.callLater(() => { - var sp = PanelService.getPanel("setupWizardPanel", targetScreen) - if (sp) - sp.open() - }) - } - } + showSetupWizard() } else { Settings.data.setupCompleted = true } } + + function showSetupWizard() { + // Open Setup Wizard as a panel in the same windowing system as Settings/ControlCenter + if (Quickshell.screens.length > 0) { + var targetScreen = Quickshell.screens[0] + var setupPanel = PanelService.getPanel("setupWizardPanel", targetScreen) + if (setupPanel) { + setupPanel.open() + } else { + // If not yet loaded, ensure it loads and try again shortly + Qt.callLater(() => { + showSetupWizard() + }) + } + } + } } From 5c93d4a41a2cf12bcd6f2a17dcf71d0644678068 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Fri, 7 Nov 2025 22:42:50 +0100 Subject: [PATCH 04/21] SettingsPanel: add NListView to enable scrolling in sidepanel if needed --- Modules/Panels/Settings/SettingsPanel.qml | 149 +++++++++++----------- Widgets/NListView.qml | 53 +------- 2 files changed, 82 insertions(+), 120 deletions(-) diff --git a/Modules/Panels/Settings/SettingsPanel.qml b/Modules/Panels/Settings/SettingsPanel.qml index 40f8531e..540a75d2 100644 --- a/Modules/Panels/Settings/SettingsPanel.qml +++ b/Modules/Panels/Settings/SettingsPanel.qml @@ -360,92 +360,93 @@ SmartPanel { border.width: Style.borderS radius: Style.radiusM - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.NoButton // Don't interfere with clicks - property int wheelAccumulator: 0 - onWheel: wheel => { - wheelAccumulator += wheel.angleDelta.y - if (wheelAccumulator >= 120) { - root.selectPreviousTab() - wheelAccumulator = 0 - } else if (wheelAccumulator <= -120) { - root.selectNextTab() - wheelAccumulator = 0 - } - wheel.accepted = true - } - } - - ColumnLayout { + NListView { + id: sidebarList anchors.fill: parent anchors.margins: Style.marginS + model: root.tabsModel spacing: Style.marginXS + currentIndex: root.currentTabIndex + verticalPolicy: ScrollBar.AsNeeded - Repeater { - id: sections - model: root.tabsModel - delegate: Rectangle { - id: tabItem - Layout.fillWidth: true - Layout.preferredHeight: tabEntryRow.implicitHeight + Style.marginM * 2 - radius: Style.radiusS - color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mHover : Color.transparent) - readonly property bool selected: index === currentTabIndex - property bool hovering: false - property color tabTextColor: selected ? Color.mOnPrimary : (tabItem.hovering ? Color.mOnHover : Color.mOnSurface) + delegate: Rectangle { + id: tabItem + width: sidebarList.verticalScrollBarActive ? sidebarList.width - sidebarList.scrollBarWidth - Style.marginXS : sidebarList.width + height: tabEntryRow.implicitHeight + Style.marginM * 2 + radius: Style.radiusS + color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mHover : Color.transparent) + readonly property bool selected: index === root.currentTabIndex + property bool hovering: false + property color tabTextColor: selected ? Color.mOnPrimary : (tabItem.hovering ? Color.mOnHover : Color.mOnSurface) - Behavior on color { - ColorAnimation { - duration: Style.animationFast - } + Behavior on width { + NumberAnimation { + duration: Style.animationFast + } + } + + Behavior on color { + ColorAnimation { + duration: Style.animationFast + } + } + + Behavior on tabTextColor { + ColorAnimation { + duration: Style.animationFast + } + } + + RowLayout { + id: tabEntryRow + anchors.fill: parent + anchors.leftMargin: Style.marginS + anchors.rightMargin: Style.marginS + spacing: Style.marginM + + // Tab icon + NIcon { + icon: modelData.icon + color: tabTextColor + pointSize: Style.fontSizeXL } - Behavior on tabTextColor { - ColorAnimation { - duration: Style.animationFast - } + // Tab label + NText { + text: I18n.tr(modelData.label) + color: tabTextColor + pointSize: Style.fontSizeM + font.weight: Style.fontWeightBold + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } + } - RowLayout { - id: tabEntryRow - anchors.fill: parent - anchors.leftMargin: Style.marginS - anchors.rightMargin: Style.marginS - spacing: Style.marginM - - // Tab icon - NIcon { - icon: modelData.icon - color: tabTextColor - pointSize: Style.fontSizeXL - } - - // Tab label - NText { - text: I18n.tr(modelData.label) - color: tabTextColor - pointSize: Style.fontSizeM - font.weight: Style.fontWeightBold - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton - onEntered: tabItem.hovering = true - onExited: tabItem.hovering = false - onCanceled: tabItem.hovering = false - onClicked: currentTabIndex = index - } + MouseArea { + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton + onEntered: tabItem.hovering = true + onExited: tabItem.hovering = false + onCanceled: tabItem.hovering = false + onClicked: root.currentTabIndex = index } } - Item { - Layout.fillHeight: true + onCurrentIndexChanged: { + if (currentIndex !== root.currentTabIndex) { + root.currentTabIndex = currentIndex + } + } + + Connections { + target: root + function onCurrentTabIndexChanged() { + if (sidebarList.currentIndex !== root.currentTabIndex) { + sidebarList.currentIndex = root.currentTabIndex + sidebarList.positionViewAtIndex(root.currentTabIndex, ListView.Contain) + } + } } } } diff --git a/Widgets/NListView.qml b/Widgets/NListView.qml index 9545bd2e..5fc13b90 100644 --- a/Widgets/NListView.qml +++ b/Widgets/NListView.qml @@ -13,7 +13,13 @@ Item { property real handleWidth: 6 property real handleRadius: Style.radiusM property int verticalPolicy: ScrollBar.AsNeeded - property int horizontalPolicy: ScrollBar.AsNeeded + property int horizontalPolicy: ScrollBar.AlwaysOff + readonly property bool verticalScrollBarActive: { + if (listView.ScrollBar.vertical.policy === ScrollBar.AlwaysOff) + return false + return listView.contentHeight > listView.height + } + readonly property real scrollBarWidth: verticalScrollBarActive ? handleWidth : 0 // Forward ListView properties property alias model: listView.model @@ -118,7 +124,6 @@ Item { x: listView.mirrored ? 0 : listView.width - width y: 0 height: listView.height - active: listView.ScrollBar.horizontal.active policy: root.verticalPolicy contentItem: Rectangle { @@ -155,49 +160,5 @@ Item { } } } - - ScrollBar.horizontal: ScrollBar { - id: horizontalScrollBar - parent: listView - x: 0 - y: listView.height - height - width: listView.width - active: listView.ScrollBar.vertical.active - policy: root.horizontalPolicy - - contentItem: Rectangle { - implicitWidth: 100 - implicitHeight: root.handleWidth - radius: root.handleRadius - color: parent.pressed ? root.handlePressedColor : parent.hovered ? root.handleHoverColor : root.handleColor - opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 1.0 : 0.0 - - Behavior on opacity { - NumberAnimation { - duration: Style.animationFast - } - } - - Behavior on color { - ColorAnimation { - duration: Style.animationFast - } - } - } - - background: Rectangle { - implicitWidth: 100 - implicitHeight: root.handleWidth - color: root.trackColor - opacity: parent.policy === ScrollBar.AlwaysOn || parent.active ? 0.3 : 0.0 - radius: root.handleRadius / 2 - - Behavior on opacity { - NumberAnimation { - duration: Style.animationFast - } - } - } - } } } From b0ea80525ac94e5202b29e8c6dc6d2e62e6a9480 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 16:56:15 -0500 Subject: [PATCH 05/21] Notification: fixed positionning. --- Modules/Notification/Notification.qml | 62 +++++++++++++++++---------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/Modules/Notification/Notification.qml b/Modules/Notification/Notification.qml index a0dc4849..c8ffce5d 100644 --- a/Modules/Notification/Notification.qml +++ b/Modules/Notification/Notification.qml @@ -58,23 +58,38 @@ Variants { readonly property bool isRight: location.endsWith("_right") readonly property bool isCentered: location === "top" || location === "bottom" + readonly property string barPos: Settings.data.bar.position + readonly property bool isFloating: Settings.data.bar.floating + readonly property int notifWidth: Math.round(420 * Style.uiScaleRatio) - // Calculate bar offset once - readonly property int barOffset: { - const barPos = Settings.data.bar.position - const isFloating = Settings.data.bar.floating + // Calculate bar offsets for each edge separately + readonly property int barOffsetTop: { + if (barPos !== "top") + return 0 const floatMarginV = isFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0 - const floatMarginH = isFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0 + return Style.barHeight + floatMarginV + } - // Check if bar is on same edge as notifications - if ((isTop && barPos === "top") || (isBottom && barPos === "bottom")) { - return Style.barHeight + floatMarginV - } - if ((isLeft && barPos === "left") || (isRight && barPos === "right")) { - return Style.barHeight + floatMarginH - } - return 0 + readonly property int barOffsetBottom: { + if (barPos !== "bottom") + return 0 + const floatMarginV = isFloating ? Settings.data.bar.marginVertical * Style.marginXL : 0 + return Style.barHeight + floatMarginV + } + + readonly property int barOffsetLeft: { + if (barPos !== "left") + return 0 + const floatMarginH = isFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0 + return floatMarginH + } + + readonly property int barOffsetRight: { + if (barPos !== "right") + return 0 + const floatMarginH = isFloating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0 + return floatMarginH } // Anchoring @@ -83,11 +98,11 @@ Variants { anchors.left: isLeft anchors.right: isRight - // Margins for PanelWindow (not anchors.topMargin!) - margins.top: isTop ? Style.marginM + barOffset : 0 - margins.bottom: isBottom ? Style.marginM + barOffset : 0 - margins.left: isLeft ? Style.marginM + barOffset : 0 - margins.right: isRight ? Style.marginM + barOffset : 0 + // Margins for PanelWindow - only apply bar offset for the specific edge where the bar is + margins.top: isTop ? Style.marginM + barOffsetTop : 0 + margins.bottom: isBottom ? Style.marginM + barOffsetBottom : 0 + margins.left: isLeft ? Style.marginM + barOffsetLeft : 0 + margins.right: isRight ? Style.marginM + barOffsetRight : 0 implicitWidth: notifWidth implicitHeight: notificationStack.implicitHeight + Style.marginL @@ -186,7 +201,6 @@ Variants { id: cardBackground anchors.fill: parent anchors.margins: Style.marginM - anchors.rightMargin: Style.marginXL radius: Style.radiusL border.color: Qt.alpha(Color.mOutline, Settings.data.notifications.backgroundOpacity || 1.0) border.width: Style.borderS @@ -373,7 +387,6 @@ Variants { id: notificationContent anchors.fill: parent anchors.margins: Style.marginM - anchors.rightMargin: Style.marginXL spacing: Style.marginM RowLayout { @@ -433,10 +446,11 @@ Variants { color: Color.mOnSurface textFormat: Text.PlainText wrapMode: Text.WrapAtWordBoundaryOrAnywhere - Layout.fillWidth: true maximumLineCount: 3 elide: Text.ElideRight visible: text.length > 0 + Layout.fillWidth: true + Layout.rightMargin: Style.marginM } NText { @@ -445,10 +459,12 @@ Variants { color: Color.mOnSurface textFormat: Text.PlainText wrapMode: Text.WrapAtWordBoundaryOrAnywhere - Layout.fillWidth: true + maximumLineCount: 5 elide: Text.ElideRight visible: text.length > 0 + Layout.fillWidth: true + Layout.rightMargin: Style.marginXL } // Actions @@ -508,7 +524,7 @@ Variants { anchors.top: parent.top anchors.topMargin: Style.marginXL anchors.right: parent.right - anchors.rightMargin: Style.marginXL * 1.5 + anchors.rightMargin: Style.marginXL onClicked: { NotificationService.removeFromHistory(model.id) From 6e7407ebf9b02adadbba063ea48dabe9456c9ed6 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 16:57:19 -0500 Subject: [PATCH 06/21] SettingsPanel: rounding coordinates --- Modules/Panels/Settings/SettingsPanel.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Panels/Settings/SettingsPanel.qml b/Modules/Panels/Settings/SettingsPanel.qml index 540a75d2..f1694ab0 100644 --- a/Modules/Panels/Settings/SettingsPanel.qml +++ b/Modules/Panels/Settings/SettingsPanel.qml @@ -12,13 +12,13 @@ import qs.Modules.MainScreen SmartPanel { id: root - preferredWidth: 820 * Style.uiScaleRatio - preferredHeight: 900 * Style.uiScaleRatio + preferredWidth: Math.round(820 * Style.uiScaleRatio) + preferredHeight: Math.round(900 * Style.uiScaleRatio) + readonly property bool attachToBar: Settings.data.ui.settingsPanelAttachToBar readonly property string barPosition: Settings.data.bar.position forceAttachToBar: attachToBar - panelAnchorHorizontalCenter: attachToBar ? (barPosition === "top" || barPosition === "bottom") : true panelAnchorVerticalCenter: attachToBar ? (barPosition === "left" || barPosition === "right") : true panelAnchorTop: attachToBar && barPosition === "top" From 363c639122729daf0417ccc3891e1f9ea9374721 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 17:05:17 -0500 Subject: [PATCH 07/21] 3.0.1 --- Services/UpdateService.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index 9485f76c..352cc99b 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -8,8 +8,8 @@ Singleton { id: root // Public properties - property string baseVersion: "3.0.0" - property bool isDevelopment: true + property string baseVersion: "3.0.1" + property bool isDevelopment: false property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From cece0dd8df732ad603f75aecc7dead532610e4bd Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 17:06:14 -0500 Subject: [PATCH 08/21] Back to -dev --- Services/UpdateService.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index 352cc99b..3224fb06 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -9,7 +9,7 @@ Singleton { // Public properties property string baseVersion: "3.0.1" - property bool isDevelopment: false + property bool isDevelopment: true property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From 6915f80c5bbcb81feaed30bb0c8984dde1609e1a Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 17:37:58 -0500 Subject: [PATCH 09/21] MainScreen: Add right and middle click to auto close panels --- Modules/MainScreen/MainScreen.qml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Modules/MainScreen/MainScreen.qml b/Modules/MainScreen/MainScreen.qml index 3b1ff16c..48be213f 100644 --- a/Modules/MainScreen/MainScreen.qml +++ b/Modules/MainScreen/MainScreen.qml @@ -174,11 +174,12 @@ PanelWindow { MouseArea { anchors.fill: parent enabled: root.isPanelOpen - onClicked: { - if (PanelService.openedPanel) { - PanelService.openedPanel.close() - } - } + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + onClicked: mouse => { + if (PanelService.openedPanel) { + PanelService.openedPanel.close() + } + } z: 0 // Behind panels and bar } From 48ff3314cc5d0f836f68f9b5b3d358b81815a7d6 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 17:38:15 -0500 Subject: [PATCH 10/21] NotificationHistory: Break the binding loop --- .../NotificationHistoryPanel.qml | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml b/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml index dd1c87a8..78c6fbbf 100644 --- a/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml +++ b/Modules/Panels/NotificationHistory/NotificationHistoryPanel.qml @@ -144,16 +144,14 @@ SmartPanel { property string notificationId: model.id property bool isExpanded: notificationList.expandedId === notificationId - width: notificationList.width - height: notificationLayoutWrapper.height + (Style.marginM * 2) + // Cache the content height to break binding loops + property real contentHeight: notificationLayout.implicitHeight - Behavior on height { - enabled: !Settings.data.general.animationDisabled - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad - } - } + // Cache truncation state to avoid binding to truncated property during polish + property bool hasTextTruncated: false + + width: notificationList.width + height: contentHeight + (Style.marginM * 2) Rectangle { anchors.fill: parent @@ -176,7 +174,7 @@ SmartPanel { anchors.fill: parent // Don't capture clicks on the delete button anchors.rightMargin: 48 - enabled: (summaryText.truncated || bodyText.truncated) + enabled: hasTextTruncated onClicked: { if (notificationList.expandedId === notificationId) { notificationList.expandedId = "" @@ -187,6 +185,25 @@ SmartPanel { cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor } + // Update truncation state asynchronously + Connections { + target: summaryText + function onTruncatedChanged() { + hasTextTruncated = summaryText.truncated || bodyText.truncated + } + } + + Connections { + target: bodyText + function onTruncatedChanged() { + hasTextTruncated = summaryText.truncated || bodyText.truncated + } + } + + Component.onCompleted: { + hasTextTruncated = summaryText.truncated || bodyText.truncated + } + Item { id: notificationLayoutWrapper anchors.left: parent.left @@ -270,6 +287,11 @@ SmartPanel { Layout.fillWidth: true maximumLineCount: isExpanded ? 999 : 2 elide: Text.ElideRight + + // Smooth transition without triggering layout recalculation + Behavior on maximumLineCount { + enabled: false // Disable animation on this to avoid polish loops + } } // Body @@ -284,18 +306,24 @@ SmartPanel { maximumLineCount: isExpanded ? 999 : 3 elide: Text.ElideRight visible: text.length > 0 + + // Smooth transition without triggering layout recalculation + Behavior on maximumLineCount { + enabled: false // Disable animation on this to avoid polish loops + } } // Spacer for expand indicator Item { Layout.fillWidth: true - Layout.preferredHeight: (!isExpanded && (summaryText.truncated || bodyText.truncated)) ? (Style.marginS) : 0 + Layout.preferredHeight: Style.marginS + visible: !isExpanded && hasTextTruncated } // Expand indicator RowLayout { Layout.fillWidth: true - visible: !isExpanded && (summaryText.truncated || bodyText.truncated) + visible: !isExpanded && hasTextTruncated spacing: Style.marginXS Item { From fa61925982f827228084afb8b23739b277694882 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 19:32:10 -0500 Subject: [PATCH 11/21] MainScreen: Only dim & mask if panel is opened on 'this' screen. --- Modules/Bar/Widgets/Workspace.qml | 4 ++-- Modules/MainScreen/MainScreen.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Bar/Widgets/Workspace.qml b/Modules/Bar/Widgets/Workspace.qml index a0ec3048..0ceec88e 100644 --- a/Modules/Bar/Widgets/Workspace.qml +++ b/Modules/Bar/Widgets/Workspace.qml @@ -54,8 +54,8 @@ Item { property bool effectsActive: false property color effectColor: Color.mPrimary - property int horizontalPadding: Math.round(Style.marginS) - property int spacingBetweenPills: Math.round(Style.marginXS) + property int horizontalPadding: Style.marginS + property int spacingBetweenPills: Style.marginXS // Wheel scroll handling property int wheelAccumulatedDelta: 0 diff --git a/Modules/MainScreen/MainScreen.qml b/Modules/MainScreen/MainScreen.qml index 48be213f..78cf25e4 100644 --- a/Modules/MainScreen/MainScreen.qml +++ b/Modules/MainScreen/MainScreen.qml @@ -78,7 +78,7 @@ PanelWindow { // Desktop dimming when panels are open property bool dimDesktop: Settings.data.general.dimDesktop - property bool isPanelOpen: PanelService.openedPanel !== null + property bool isPanelOpen: (PanelService.openedPanel !== null) && (PanelService.openedPanel.screen === screen) color: { if (dimDesktop && isPanelOpen) { return Qt.alpha(Color.mShadow, 0.8) From a41ac4701f8644ae1569bdab204402479a737572 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 20:22:48 -0500 Subject: [PATCH 12/21] Optimizations --- .../ControlCenter/Cards/WeatherCard.qml | 9 ++- Widgets/NBusyIndicator.qml | 60 +++++++++++-------- Widgets/NCircleStat.qml | 40 +++++++++++-- Widgets/NSlider.qml | 33 ++-------- 4 files changed, 79 insertions(+), 63 deletions(-) diff --git a/Modules/Panels/ControlCenter/Cards/WeatherCard.qml b/Modules/Panels/ControlCenter/Cards/WeatherCard.qml index 737624ba..b9bd0344 100644 --- a/Modules/Panels/ControlCenter/Cards/WeatherCard.qml +++ b/Modules/Panels/ControlCenter/Cards/WeatherCard.qml @@ -134,11 +134,10 @@ NBox { } } - RowLayout { - visible: !weatherReady - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - NBusyIndicator {} + Loader { + active: !weatherReady + Layout.alignment: Qt.AlignCenter + sourceComponent: NBusyIndicator {} } } } diff --git a/Widgets/NBusyIndicator.qml b/Widgets/NBusyIndicator.qml index bf93abe8..f65da231 100644 --- a/Widgets/NBusyIndicator.qml +++ b/Widgets/NBusyIndicator.qml @@ -14,40 +14,50 @@ Item { implicitWidth: size implicitHeight: size - Canvas { - id: canvas + // GPU-optimized spinner - draw once, rotate with GPU transform + Item { + id: spinner anchors.fill: parent - onPaint: { - var ctx = getContext("2d") - ctx.reset() + // Static canvas - drawn ONCE, then cached + Canvas { + id: canvas + anchors.fill: parent + renderStrategy: Canvas.Cooperative // Better performance than Threaded for simple shapes + renderTarget: Canvas.FramebufferObject // GPU texture - var centerX = width / 2 - var centerY = height / 2 - var radius = Math.min(width, height) / 2 - strokeWidth / 2 + // Enable layer caching - critical for performance! + layer.enabled: true + layer.smooth: true - ctx.strokeStyle = root.color - ctx.lineWidth = Math.max(1, root.strokeWidth) - ctx.lineCap = "round" + Component.onCompleted: { + requestPaint() + } - // Draw arc with gap (270 degrees with 90 degree gap) - ctx.beginPath() - ctx.arc(centerX, centerY, radius, -Math.PI / 2 + rotationAngle, -Math.PI / 2 + rotationAngle + Math.PI * 1.5) - ctx.stroke() + onPaint: { + var ctx = getContext("2d") + ctx.reset() + + var centerX = width / 2 + var centerY = height / 2 + var radius = Math.min(width, height) / 2 - strokeWidth / 2 + + ctx.strokeStyle = root.color + ctx.lineWidth = Math.max(1, root.strokeWidth) + ctx.lineCap = "round" + + // Draw arc with gap (270 degrees = 3/4 of circle) + ctx.beginPath() + ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + Math.PI * 1.5) + ctx.stroke() + } } - property real rotationAngle: 0 - - onRotationAngleChanged: { - requestPaint() - } - - NumberAnimation { - target: canvas - property: "rotationAngle" + // Smooth rotation animation - uses GPU transform, NO canvas repaints! + RotationAnimation on rotation { running: root.running from: 0 - to: 2 * Math.PI + to: 360 duration: root.duration loops: Animation.Infinite } diff --git a/Widgets/NCircleStat.qml b/Widgets/NCircleStat.qml index ad5a6971..169aa445 100644 --- a/Widgets/NCircleStat.qml +++ b/Widgets/NCircleStat.qml @@ -24,8 +24,27 @@ Rectangle { border.color: flat ? Color.transparent : Color.mSurfaceVariant border.width: flat ? 0 : Style.borderS - // Repaint gauge when the bound value changes - onValueChanged: gauge.requestPaint() + // Animated value for smooth transitions - reduces repaint frequency + property real animatedValue: value + + Behavior on animatedValue { + enabled: !Settings.data.general.animationDisabled + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutCubic + } + } + + // Repaint gauge when animated value changes (throttled by animation) + onAnimatedValueChanged: repaintTimer.restart() + + // Debounce timer to limit repaint frequency during rapid value changes + Timer { + id: repaintTimer + interval: 16 // ~60 FPS max + repeat: false + onTriggered: gauge.requestPaint() + } ColumnLayout { id: mainLayout @@ -45,7 +64,18 @@ Rectangle { Canvas { id: gauge anchors.fill: parent - renderStrategy: Canvas.Immediate + + // Optimized Canvas settings for better GPU performance + renderStrategy: Canvas.Cooperative // Better performance than Immediate + renderTarget: Canvas.FramebufferObject // GPU texture rendering + + // Enable layer caching - critical for performance! + layer.enabled: true + layer.smooth: true + + Component.onCompleted: { + requestPaint() + } onPaint: { const ctx = getContext("2d") @@ -68,7 +98,7 @@ Rectangle { ctx.stroke() // Value arc with gradient starting at 25% - const ratio = Math.max(0, Math.min(1, root.value / 100)) + const ratio = Math.max(0, Math.min(1, root.animatedValue / 100)) const end = start + (endBg - start) * ratio // Calculate gradient start point (25% into the arc) @@ -97,7 +127,7 @@ Rectangle { id: valueLabel anchors.centerIn: parent anchors.verticalCenterOffset: -4 * contentScale - text: `${root.value}${root.suffix}` + text: `${Math.round(root.value)}${root.suffix}` pointSize: Style.fontSizeM * contentScale * 0.9 font.weight: Style.fontWeightBold color: Color.mOnSurface diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index 994ced73..82bbf102 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -7,6 +7,7 @@ import qs.Services Slider { id: root + property color fillColor: Color.mPrimary property var cutoutColor: Color.mSurface property bool snapAlways: true property real heightRatio: 0.7 @@ -46,7 +47,7 @@ Slider { width: parent.height height: parent.height radius: width / 2 - color: Qt.darker(Color.mPrimary, 1.2) //starting color of gradient + color: Qt.darker(fillColor, 1.2) //starting color of gradient } // The main rectangle @@ -60,35 +61,11 @@ Slider { orientation: Gradient.Horizontal GradientStop { position: 0.0 - color: Qt.darker(Color.mPrimary, 1.2) - Behavior on color { - ColorAnimation { - duration: 300 - } - } - } - GradientStop { - position: 0.5 - color: Color.mPrimary - SequentialAnimation on position { - loops: Animation.Infinite - NumberAnimation { - from: 0.3 - to: 0.7 - duration: 2000 - easing.type: Easing.InOutSine - } - NumberAnimation { - from: 0.7 - to: 0.3 - duration: 2000 - easing.type: Easing.InOutSine - } - } + color: Qt.darker(fillColor, 1.2) } GradientStop { position: 1.0 - color: Qt.lighter(Color.mPrimary, 1.2) + color: fillColor } } } @@ -118,7 +95,7 @@ Slider { implicitHeight: knobDiameter radius: width / 2 color: root.pressed ? Color.mHover : Color.mSurface - border.color: Color.mPrimary + border.color: fillColor border.width: Style.borderL anchors.centerIn: parent From f6a5b1a2fd696b51ce94b79e2a3821ba53716a29 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 21:19:56 -0500 Subject: [PATCH 13/21] MediaCard: lots of GPU optimization + show the proper wallpaper in the media card if no mpris. --- .../Panels/ControlCenter/Cards/MediaCard.qml | 377 ++++++++---------- 1 file changed, 167 insertions(+), 210 deletions(-) diff --git a/Modules/Panels/ControlCenter/Cards/MediaCard.qml b/Modules/Panels/ControlCenter/Cards/MediaCard.qml index 87d56d95..9879d84c 100644 --- a/Modules/Panels/ControlCenter/Cards/MediaCard.qml +++ b/Modules/Panels/ControlCenter/Cards/MediaCard.qml @@ -11,10 +11,14 @@ import qs.Widgets NBox { id: root + // Track whether we have an active media player + readonly property bool hasActivePlayer: MediaService.currentPlayer && MediaService.canPlay + // Wrapper - rounded rect clipper Item { anchors.fill: parent layer.enabled: true + layer.smooth: true layer.effect: MultiEffect { maskEnabled: true maskThresholdMin: 0.5 @@ -34,16 +38,9 @@ NBox { readonly property int dim: Math.round(256 * Style.uiScaleRatio) id: bgImage anchors.fill: parent - source: MediaService.trackArtUrl || WallpaperService.getWallpaper(Screen.name) + source: MediaService.trackArtUrl || WallpaperService.getWallpaper(screen.name) sourceSize: Qt.size(dim, dim) fillMode: Image.PreserveAspectCrop - - layer.enabled: true - layer.effect: MultiEffect { - blurEnabled: true - blur: 0.25 - blurMax: 16 - } } // Dark overlay for readability @@ -189,245 +186,205 @@ NBox { anchors.fill: parent anchors.margins: Style.marginM - // No media player detected - ColumnLayout { - id: fallback + // No media player detected - use Loader for performance + Loader { + id: fallbackLoader + Layout.fillWidth: true + Layout.fillHeight: true + active: !root.hasActivePlayer - visible: !main.visible - spacing: Style.marginS + sourceComponent: ColumnLayout { + spacing: Style.marginS - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } - Item { - Layout.fillWidth: true - Layout.fillHeight: true + Item { + Layout.fillWidth: true - ColumnLayout { - anchors.centerIn: parent - spacing: Style.marginL + ColumnLayout { + anchors.centerIn: parent + spacing: Style.marginL - Item { - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: Style.fontSizeXXXL * 4 - Layout.preferredHeight: Style.fontSizeXXXL * 4 + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: Style.fontSizeXXXL * 4 + Layout.preferredHeight: Style.fontSizeXXXL * 4 - // Pulsating audio circles (background) - Repeater { - model: 3 - Rectangle { + // Center icon (static, no animation to avoid GPU cost) + NIcon { anchors.centerIn: parent - width: parent.width * (1.0 + index * 0.2) - height: width - radius: width / 2 - color: "transparent" - border.color: Color.mOnSurfaceVariant - border.width: 2 - opacity: 0 - - SequentialAnimation on opacity { - running: true - loops: Animation.Infinite - PauseAnimation { - duration: index * 600 - } - NumberAnimation { - from: 1.0 - to: 0 - duration: 2000 - easing.type: Easing.OutQuad - } - } - - SequentialAnimation on scale { - running: true - loops: Animation.Infinite - PauseAnimation { - duration: index * 600 - } - NumberAnimation { - from: 0.6 - to: 1.2 - duration: 2000 - easing.type: Easing.OutQuad - } - } + icon: "disc" + pointSize: Style.fontSizeXXXL * 3 + color: Color.mOnSurfaceVariant + opacity: 1.0 } } - - // Spinning disc - NIcon { - anchors.centerIn: parent - icon: "disc" - pointSize: Style.fontSizeXXXL * 3 - color: Color.mOnSurfaceVariant - } - } - - // Descriptive text - ColumnLayout { - Layout.alignment: Qt.AlignHCenter - spacing: Style.marginXS } } - } - Item { - Layout.fillWidth: true - Layout.fillHeight: true + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } } } - // MediaPlayer Main Content - ColumnLayout { - id: main + // MediaPlayer Main Content - use Loader for performance + Loader { + id: mainLoader + Layout.fillWidth: true + Layout.fillHeight: true + active: root.hasActivePlayer - visible: MediaService.currentPlayer && MediaService.canPlay - spacing: Style.marginS + sourceComponent: ColumnLayout { + id: main + spacing: Style.marginS - // Spacer to push content down - Item { - Layout.preferredHeight: Style.marginM - } + // Spacer to push content down + Item { + Layout.preferredHeight: Style.marginM + } - // Metadata at the bottom left - ColumnLayout { - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft - spacing: Style.marginXS - - NText { - visible: MediaService.trackTitle !== "" - text: MediaService.trackTitle - pointSize: Style.fontSizeL - font.weight: Style.fontWeightBold - elide: Text.ElideRight - wrapMode: Text.Wrap - maximumLineCount: 2 + // Metadata at the bottom left + ColumnLayout { Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + spacing: Style.marginXS + + NText { + visible: MediaService.trackTitle !== "" + text: MediaService.trackTitle + pointSize: Style.fontSizeL + font.weight: Style.fontWeightBold + elide: Text.ElideRight + wrapMode: Text.Wrap + maximumLineCount: 2 + Layout.fillWidth: true + } + + NText { + visible: MediaService.trackArtist !== "" + text: MediaService.trackArtist + color: Color.mPrimary + pointSize: Style.fontSizeS + elide: Text.ElideRight + Layout.fillWidth: true + } + + NText { + visible: MediaService.trackAlbum !== "" + text: MediaService.trackAlbum + color: Color.mOnSurfaceVariant + pointSize: Style.fontSizeM + elide: Text.ElideRight + Layout.fillWidth: true + } } - NText { - visible: MediaService.trackArtist !== "" - text: MediaService.trackArtist - color: Color.mPrimary - pointSize: Style.fontSizeS - elide: Text.ElideRight + // Progress slider + Item { + id: progressWrapper + visible: (MediaService.currentPlayer && MediaService.trackLength > 0) Layout.fillWidth: true - } + height: Style.baseWidgetSize * 0.5 - NText { - visible: MediaService.trackAlbum !== "" - text: MediaService.trackAlbum - color: Color.mOnSurfaceVariant - pointSize: Style.fontSizeM - elide: Text.ElideRight - Layout.fillWidth: true - } - } + property real localSeekRatio: -1 + property real lastSentSeekRatio: -1 + property real seekEpsilon: 0.01 + property real progressRatio: { + if (!MediaService.currentPlayer || MediaService.trackLength <= 0) + return 0 + const r = MediaService.currentPosition / MediaService.trackLength + if (isNaN(r) || !isFinite(r)) + return 0 + return Math.max(0, Math.min(1, r)) + } + property real effectiveRatio: (MediaService.isSeeking && localSeekRatio >= 0) ? Math.max(0, Math.min(1, localSeekRatio)) : progressRatio - // Progress slider - Item { - id: progressWrapper - visible: (MediaService.currentPlayer && MediaService.trackLength > 0) - Layout.fillWidth: true - height: Style.baseWidgetSize * 0.5 - - property real localSeekRatio: -1 - property real lastSentSeekRatio: -1 - property real seekEpsilon: 0.01 - property real progressRatio: { - if (!MediaService.currentPlayer || MediaService.trackLength <= 0) - return 0 - const r = MediaService.currentPosition / MediaService.trackLength - if (isNaN(r) || !isFinite(r)) - return 0 - return Math.max(0, Math.min(1, r)) - } - property real effectiveRatio: (MediaService.isSeeking && localSeekRatio >= 0) ? Math.max(0, Math.min(1, localSeekRatio)) : progressRatio - - Timer { - id: seekDebounce - interval: 75 - repeat: false - onTriggered: { - if (MediaService.isSeeking && progressWrapper.localSeekRatio >= 0) { - const next = Math.max(0, Math.min(1, progressWrapper.localSeekRatio)) - if (progressWrapper.lastSentSeekRatio < 0 || Math.abs(next - progressWrapper.lastSentSeekRatio) >= progressWrapper.seekEpsilon) { - MediaService.seekByRatio(next) - progressWrapper.lastSentSeekRatio = next + Timer { + id: seekDebounce + interval: 75 + repeat: false + onTriggered: { + if (MediaService.isSeeking && progressWrapper.localSeekRatio >= 0) { + const next = Math.max(0, Math.min(1, progressWrapper.localSeekRatio)) + if (progressWrapper.lastSentSeekRatio < 0 || Math.abs(next - progressWrapper.lastSentSeekRatio) >= progressWrapper.seekEpsilon) { + MediaService.seekByRatio(next) + progressWrapper.lastSentSeekRatio = next + } } } } - } - NSlider { - id: progressSlider - anchors.fill: parent - from: 0 - to: 1 - stepSize: 0 - snapAlways: false - enabled: MediaService.trackLength > 0 && MediaService.canSeek - heightRatio: 0.6 + NSlider { + id: progressSlider + anchors.fill: parent + from: 0 + to: 1 + stepSize: 0 + snapAlways: false + enabled: MediaService.trackLength > 0 && MediaService.canSeek + heightRatio: 0.6 - onMoved: { - progressWrapper.localSeekRatio = value - seekDebounce.restart() - } - onPressedChanged: { - if (pressed) { - MediaService.isSeeking = true + onMoved: { progressWrapper.localSeekRatio = value - MediaService.seekByRatio(value) - progressWrapper.lastSentSeekRatio = value - } else { - seekDebounce.stop() - MediaService.seekByRatio(value) - MediaService.isSeeking = false - progressWrapper.localSeekRatio = -1 - progressWrapper.lastSentSeekRatio = -1 + seekDebounce.restart() + } + onPressedChanged: { + if (pressed) { + MediaService.isSeeking = true + progressWrapper.localSeekRatio = value + MediaService.seekByRatio(value) + progressWrapper.lastSentSeekRatio = value + } else { + seekDebounce.stop() + MediaService.seekByRatio(value) + MediaService.isSeeking = false + progressWrapper.localSeekRatio = -1 + progressWrapper.lastSentSeekRatio = -1 + } } } + + Binding { + target: progressSlider + property: "value" + value: progressWrapper.progressRatio + when: !MediaService.isSeeking + } } - Binding { - target: progressSlider - property: "value" - value: progressWrapper.progressRatio - when: !MediaService.isSeeking - } - } - - // Spacer to push media controls down - Item { - Layout.preferredHeight: Style.marginL - } - - // Media controls - RowLayout { - spacing: Style.marginS - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - - NIconButton { - icon: "media-prev" - visible: MediaService.canGoPrevious - onClicked: MediaService.canGoPrevious ? MediaService.previous() : {} + // Spacer to push media controls down + Item { + Layout.preferredHeight: Style.marginL } - NIconButton { - icon: MediaService.isPlaying ? "media-pause" : "media-play" - visible: (MediaService.canPlay || MediaService.canPause) - onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {} - } + // Media controls + RowLayout { + spacing: Style.marginS + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter - NIconButton { - icon: "media-next" - visible: MediaService.canGoNext - onClicked: MediaService.canGoNext ? MediaService.next() : {} + NIconButton { + icon: "media-prev" + visible: MediaService.canGoPrevious + onClicked: MediaService.canGoPrevious ? MediaService.previous() : {} + } + + NIconButton { + icon: MediaService.isPlaying ? "media-pause" : "media-play" + visible: (MediaService.canPlay || MediaService.canPause) + onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {} + } + + NIconButton { + icon: "media-next" + visible: MediaService.canGoNext + onClicked: MediaService.canGoNext ? MediaService.next() : {} + } } } } From 6a6d4abd2ffb280baa1ddfc5fd249724ee37c4eb Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 21:55:41 -0500 Subject: [PATCH 14/21] ControlCenterTab: fix settings dialog component path. --- Modules/Panels/Settings/Tabs/ControlCenterTab.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Panels/Settings/Tabs/ControlCenterTab.qml b/Modules/Panels/Settings/Tabs/ControlCenterTab.qml index b19548ed..8e96428f 100644 --- a/Modules/Panels/Settings/Tabs/ControlCenterTab.qml +++ b/Modules/Panels/Settings/Tabs/ControlCenterTab.qml @@ -255,7 +255,7 @@ ColumnLayout { NSectionEditor { sectionName: I18n.tr("settings.control-center.shortcuts.sectionLeft") sectionId: "left" - settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml") + settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml") maxWidgets: 5 widgetRegistry: ControlCenterWidgetRegistry widgetModel: Settings.data.controlCenter.shortcuts["left"] @@ -274,7 +274,7 @@ ColumnLayout { NSectionEditor { sectionName: I18n.tr("settings.control-center.shortcuts.sectionRight") sectionId: "right" - settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml") + settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/ControlCenter/ControlCenterWidgetSettingsDialog.qml") maxWidgets: 5 widgetRegistry: ControlCenterWidgetRegistry widgetModel: Settings.data.controlCenter.shortcuts["right"] From 385ac20241c51afffc10806e2f337d0bcb1aad00 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 22:03:11 -0500 Subject: [PATCH 15/21] Matugen: added a [templates] section or matugen would crash if the user templates is not customized. --- Services/MatugenTemplates.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Services/MatugenTemplates.qml b/Services/MatugenTemplates.qml index 005f4d77..ad9bf6a6 100644 --- a/Services/MatugenTemplates.qml +++ b/Services/MatugenTemplates.qml @@ -38,6 +38,8 @@ Singleton { var lines = [] lines.push("[config]") lines.push("") + lines.push("[templates]") + lines.push("") lines.push("# User-defined templates") lines.push("# Add your custom templates below") lines.push("# Example:") From 5c287996f2ade9f3c8e3c7b04800cfb3d9e9d219 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 22:22:43 -0500 Subject: [PATCH 16/21] Overview: resources optimization --- Modules/Background/Overview.qml | 60 ++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/Modules/Background/Overview.qml b/Modules/Background/Overview.qml index 3f3506f1..394276a3 100644 --- a/Modules/Background/Overview.qml +++ b/Modules/Background/Overview.qml @@ -17,6 +17,7 @@ Loader { required property ShellScreen modelData property string wallpaper: "" + property bool overviewIsSeen: false Component.onCompleted: { if (modelData) { @@ -25,6 +26,13 @@ Loader { setWallpaperInitial() } + Component.onDestruction: { + // Clean up resources to prevent memory leak when overviewEnabled is toggled off + timerDisableFx.stop() + bgImage.layer.enabled = false + bgImage.source = "" + } + // External state management Connections { target: WallpaperService @@ -35,6 +43,27 @@ Loader { } } + Connections { + target: CompositorService.backend + function onOverviewActiveChanged() { + if (CompositorService.backend.overviewActive) { + overviewIsSeen = true + timerDisableFx.stop() + } else { + timerDisableFx.restart() + } + } + } + + // Use to disable effects when overview is no longer necessary. + Timer { + id: timerDisableFx + interval: 2000 + onTriggered: { + overviewIsSeen = false + } + } + function setWallpaperInitial() { if (!WallpaperService || !WallpaperService.isInitialized) { Qt.callLater(setWallpaperInitial) @@ -64,23 +93,28 @@ Loader { anchors.fill: parent fillMode: Image.PreserveAspectCrop source: wallpaper - smooth: true + smooth: false mipmap: false cache: false asynchronous: true - // Image is heavily blurred, so might as well save a lot of memory here. - sourceSize: Qt.size(1280, 720) - } + visible: overviewIsSeen - MultiEffect { - anchors.fill: parent - source: bgImage - autoPaddingEnabled: false - blurEnabled: true - blur: 1.0 - blurMax: 64 - colorization: Style.opacityMedium - colorizationColor: Settings.data.colorSchemes.darkMode ? Color.mSurface : Color.mOnSurface + // Image is heavily blurred, so use low resolution to save memory + sourceSize: Qt.size(1280, 720) + + layer.enabled: overviewIsSeen + layer.smooth: false + layer.effect: MultiEffect { + blurEnabled: true + blur: 1.0 + blurMax: 32 + } + + Rectangle { + anchors.fill: parent + color: Settings.data.colorSchemes.darkMode ? Color.mSurface : Color.mOnSurface + opacity: 0.8 + } } } } From 15ff939ecd95c80b28785a44cb33075c9ff87494 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 22:45:38 -0500 Subject: [PATCH 17/21] SysMon-Bar: default to white text --- Services/BarWidgetRegistry.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index 670f96d9..69d30d39 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -134,7 +134,7 @@ Singleton { }, "SystemMonitor": { "allowUserSettings": true, - "usePrimaryColor": true, + "usePrimaryColor": false, "showCpuUsage": true, "showCpuTemp": true, "showMemoryUsage": true, From 5226b01fc13a0d8410ed2d33d75526af7de978f2 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 22:46:29 -0500 Subject: [PATCH 18/21] MainScreen: remove mask region asap so you can click on apps sooner when closing a panel --- .../{MainScreens.qml => AllScreens.qml} | 0 Modules/MainScreen/MainScreen.qml | 28 +++---------------- shell.qml | 2 +- 3 files changed, 5 insertions(+), 25 deletions(-) rename Modules/MainScreen/{MainScreens.qml => AllScreens.qml} (100%) diff --git a/Modules/MainScreen/MainScreens.qml b/Modules/MainScreen/AllScreens.qml similarity index 100% rename from Modules/MainScreen/MainScreens.qml rename to Modules/MainScreen/AllScreens.qml diff --git a/Modules/MainScreen/MainScreen.qml b/Modules/MainScreen/MainScreen.qml index 78cf25e4..5a59cbfc 100644 --- a/Modules/MainScreen/MainScreen.qml +++ b/Modules/MainScreen/MainScreen.qml @@ -79,6 +79,8 @@ PanelWindow { // Desktop dimming when panels are open property bool dimDesktop: Settings.data.general.dimDesktop property bool isPanelOpen: (PanelService.openedPanel !== null) && (PanelService.openedPanel.screen === screen) + property bool isPanelClosing: (PanelService.openedPanel !== null) && PanelService.openedPanel.isClosing + color: { if (dimDesktop && isPanelOpen) { return Qt.alpha(Color.mShadow, 0.8) @@ -124,30 +126,8 @@ PanelWindow { id: backgroundMaskRegion x: 0 y: 0 - width: root.isPanelOpen ? root.width : 0 - height: root.isPanelOpen ? root.height : 0 - intersection: Intersection.Subtract - } - } - - // Variants for panel regions - Variants { - id: panelRegions - - // Model is the list of open panels (filters out closed ones) - model: [audioPanel, controlCenterPanel, calendarPanel].filter(function (p) { - return p && p.isPanelOpen - }) - - Region { - required property var modelData - property var panelBg: modelData.panelRegion - - // Direct bindings - x: panelBg?.x ?? 0 - y: panelBg?.y ?? 0 - width: panelBg?.width ?? 0 - height: panelBg?.height ?? 0 + width: root.isPanelOpen && !isPanelClosing ? root.width : 0 + height: root.isPanelOpen && !isPanelClosing ? root.height : 0 intersection: Intersection.Subtract } } diff --git a/shell.qml b/shell.qml index 3f433162..8217e7e6 100644 --- a/shell.qml +++ b/shell.qml @@ -101,7 +101,7 @@ ShellRoot { IPCService {} // MainScreen for each screen (manages bar + all panels) - MainScreens {} + AllScreens {} } } From 4b7fbd2d067e2d0c161190fe58add14a7c04ffaf Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 23:06:32 -0500 Subject: [PATCH 19/21] v3.0.2 --- Services/UpdateService.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index 3224fb06..59481cc8 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -8,8 +8,8 @@ Singleton { id: root // Public properties - property string baseVersion: "3.0.1" - property bool isDevelopment: true + property string baseVersion: "3.0.2" + property bool isDevelopment: false property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From c1d43127b3c23b2ac524521d3fb4f0b46fc39cdf Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 7 Nov 2025 23:07:55 -0500 Subject: [PATCH 20/21] Back to -dev --- Services/UpdateService.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index 59481cc8..d36e57ea 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -9,7 +9,7 @@ Singleton { // Public properties property string baseVersion: "3.0.2" - property bool isDevelopment: false + property bool isDevelopment: true property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From 805b6345880be8a72afac8ddf9d4d021c03b271a Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 8 Nov 2025 00:28:49 -0500 Subject: [PATCH 21/21] Delete NShapedRectangle: no longer required --- Widgets/NShapedRectangle.qml | 209 ----------------------------------- 1 file changed, 209 deletions(-) delete mode 100644 Widgets/NShapedRectangle.qml diff --git a/Widgets/NShapedRectangle.qml b/Widgets/NShapedRectangle.qml deleted file mode 100644 index 309d53e7..00000000 --- a/Widgets/NShapedRectangle.qml +++ /dev/null @@ -1,209 +0,0 @@ -import QtQuick -import QtQuick.Effects -import qs.Commons - -Item { - id: root - - // Corner radius properties - property real topLeftRadius: 0 - property real topRightRadius: 0 - property real bottomLeftRadius: 0 - property real bottomRightRadius: 0 - - // Inverted corner properties (concave instead of convex) - property bool topLeftInverted: false - property bool topRightInverted: false - property bool bottomLeftInverted: false - property bool bottomRightInverted: false - - // Direction for inverted corners: "horizontal" or "vertical" - // horizontal: curves left/right (extends beyond left/right edges) - // vertical: curves up/down (extends beyond top/bottom edges) - property string topLeftInvertedDirection: "horizontal" // default: curves left - property string topRightInvertedDirection: "horizontal" // default: curves right - property string bottomLeftInvertedDirection: "horizontal" // default: curves left - property string bottomRightInvertedDirection: "horizontal" // default: curves right - - // Background color and borders - property color backgroundColor: Color.mPrimary - property color borderColor: Color.mOutline - property int borderWidth: Style.borderS - - // Check if any corner is inverted - readonly property bool hasInvertedCorners: topLeftInverted || topRightInverted || bottomLeftInverted || bottomRightInverted - - // Calculate padding needed for inverted corners based on their direction - readonly property real topPadding: Math.max((topLeftInverted && topLeftInvertedDirection === "vertical") ? topLeftRadius : 0, (topRightInverted && topRightInvertedDirection === "vertical") ? topRightRadius : 0) - readonly property real bottomPadding: Math.max((bottomLeftInverted && bottomLeftInvertedDirection === "vertical") ? bottomLeftRadius : 0, (bottomRightInverted && bottomRightInvertedDirection === "vertical") ? bottomRightRadius : 0) - readonly property real leftPadding: Math.max((topLeftInverted && topLeftInvertedDirection === "horizontal") ? topLeftRadius : 0, (bottomLeftInverted && bottomLeftInvertedDirection === "horizontal") ? bottomLeftRadius : 0) - readonly property real rightPadding: Math.max((topRightInverted && topRightInvertedDirection === "horizontal") ? topRightRadius : 0, (bottomRightInverted && bottomRightInvertedDirection === "horizontal") ? bottomRightRadius : 0) - - // Background layer - Item { - id: backgroundLayer - anchors.fill: parent - z: 0 - - // Simple rectangle for non-inverted corners (better performance) - Rectangle { - id: simpleBackground - anchors.fill: parent - color: root.backgroundColor - radius: topLeftRadius // Use topLeftRadius as default - border.width: borderWidth - border.color: borderColor - visible: !root.hasInvertedCorners - - topLeftRadius: root.topLeftRadius - topRightRadius: root.topRightRadius - bottomLeftRadius: root.bottomLeftRadius - bottomRightRadius: root.bottomRightRadius - } - - // Background with custom corners (for inverted corners) - Canvas { - id: background - anchors.fill: parent - anchors.topMargin: -root.topPadding - anchors.bottomMargin: -root.bottomPadding - anchors.leftMargin: -root.leftPadding - anchors.rightMargin: -root.rightPadding - visible: root.hasInvertedCorners - - antialiasing: true - renderTarget: Canvas.FramebufferObject - smooth: true - - onPaint: { - var ctx = getContext("2d") - ctx.reset() - - // Adjust coordinates to account for inverted corner padding - var x = root.leftPadding - var y = root.topPadding - var w = width - root.leftPadding - root.rightPadding - var h = height - root.topPadding - root.bottomPadding - - ctx.fillStyle = root.backgroundColor - ctx.beginPath() - - // Start from top left - if (topLeftInverted) { - if (topLeftInvertedDirection === "vertical") { - ctx.moveTo(x, y) - } else { - ctx.moveTo(x + topLeftRadius, y) - } - } else { - ctx.moveTo(x + topLeftRadius, y) - } - - // Top edge and top right corner - if (topRightInverted) { - if (topRightInvertedDirection === "horizontal") { - // Curves to the right - ctx.lineTo(x + w, y) - ctx.lineTo(x + w + topRightRadius, y) - ctx.quadraticCurveTo(x + w, y, x + w, y + topRightRadius) - } else { - // Curves upward - ctx.lineTo(x + w, y) - ctx.lineTo(x + w, y - topRightRadius) - ctx.quadraticCurveTo(x + w, y, x + w - topRightRadius, y) - ctx.lineTo(x + w, y) - ctx.lineTo(x + w, y + topRightRadius) - } - } else { - ctx.lineTo(x + w - topRightRadius, y) - ctx.arcTo(x + w, y, x + w, y + topRightRadius, topRightRadius) - } - - // Right edge and bottom right corner - if (bottomRightInverted) { - if (bottomRightInvertedDirection === "horizontal") { - // Curves to the right - ctx.lineTo(x + w, y + h - bottomRightRadius) - ctx.quadraticCurveTo(x + w, y + h, x + w + bottomRightRadius, y + h) - ctx.lineTo(x + w, y + h) - ctx.lineTo(x + w - bottomRightRadius, y + h) - } else { - // Curves downward - ctx.lineTo(x + w, y + h) - ctx.lineTo(x + w, y + h + bottomRightRadius) - ctx.quadraticCurveTo(x + w, y + h, x + w - bottomRightRadius, y + h) - } - } else { - ctx.lineTo(x + w, y + h - bottomRightRadius) - ctx.arcTo(x + w, y + h, x + w - bottomRightRadius, y + h, bottomRightRadius) - } - - // Bottom edge and bottom left corner - if (bottomLeftInverted) { - if (bottomLeftInvertedDirection === "horizontal") { - // Curves to the left - ctx.lineTo(x + bottomLeftRadius, y + h) - ctx.lineTo(x - bottomLeftRadius, y + h) - ctx.quadraticCurveTo(x, y + h, x, y + h - bottomLeftRadius) - } else { - // Curves downward - ctx.lineTo(x + bottomLeftRadius, y + h) - ctx.quadraticCurveTo(x, y + h, x, y + h + bottomLeftRadius) - ctx.lineTo(x, y + h + bottomLeftRadius) - } - } else { - ctx.lineTo(x + bottomLeftRadius, y + h) - ctx.arcTo(x, y + h, x, y + h - bottomLeftRadius, bottomLeftRadius) - } - - // Left edge and back to top left corner - if (topLeftInverted) { - if (topLeftInvertedDirection === "horizontal") { - // Curves to the left - ctx.lineTo(x, y + topLeftRadius) - ctx.quadraticCurveTo(x, y, x - topLeftRadius, y) - ctx.lineTo(x, y) - ctx.lineTo(x + topLeftRadius, y) - } else { - // Curves upward - ctx.lineTo(x, y + topLeftRadius) - ctx.lineTo(x, y) - ctx.lineTo(x, y - topLeftRadius) - ctx.quadraticCurveTo(x, y, x + topLeftRadius, y) - } - } else { - ctx.lineTo(x, y + topLeftRadius) - ctx.arcTo(x, y, x + topLeftRadius, y, topLeftRadius) - } - - ctx.closePath() - ctx.fill() - } - } - } - - // Content layer: for child elements - default property alias contentChildren: contentLayer.data - Item { - id: contentLayer - anchors.fill: parent - z: 1 - } - - // Trigger repaint when properties change - onTopLeftRadiusChanged: background.requestPaint() - onTopRightRadiusChanged: background.requestPaint() - onBottomLeftRadiusChanged: background.requestPaint() - onBottomRightRadiusChanged: background.requestPaint() - onTopLeftInvertedChanged: background.requestPaint() - onTopRightInvertedChanged: background.requestPaint() - onBottomLeftInvertedChanged: background.requestPaint() - onBottomRightInvertedChanged: background.requestPaint() - onTopLeftInvertedDirectionChanged: background.requestPaint() - onTopRightInvertedDirectionChanged: background.requestPaint() - onBottomLeftInvertedDirectionChanged: background.requestPaint() - onBottomRightInvertedDirectionChanged: background.requestPaint() - onBackgroundColorChanged: background.requestPaint() - onWidthChanged: background.requestPaint() - onHeightChanged: background.requestPaint() -}