From e86f4e56cb72cfb37ab545c72acc013ea1d7369a Mon Sep 17 00:00:00 2001 From: lysec Date: Sat, 11 Oct 2025 04:16:12 +0200 Subject: [PATCH] ControlCenter: revert to old layout --- Commons/Settings.qml | 4 + .../ControlCenter/Cards/PowerProfilesCard.qml | 66 +++++++++ Modules/ControlCenter/Cards/ProfileCard.qml | 113 +++++++++++++++ .../ControlCenter/Cards/SystemMonitorCard.qml | 27 ++-- Modules/ControlCenter/Cards/UtilitiesCard.qml | 66 +++++++++ Modules/ControlCenter/Cards/WeatherCard.qml | 130 ++++++++++++++++++ Modules/ControlCenter/ControlCenterPanel.qml | 80 ++++++----- Modules/Settings/SettingsPanel.qml | 14 +- Services/UpdateService.qml | 4 +- 9 files changed, 449 insertions(+), 55 deletions(-) create mode 100644 Modules/ControlCenter/Cards/PowerProfilesCard.qml create mode 100644 Modules/ControlCenter/Cards/ProfileCard.qml create mode 100644 Modules/ControlCenter/Cards/UtilitiesCard.qml create mode 100644 Modules/ControlCenter/Cards/WeatherCard.qml diff --git a/Commons/Settings.qml b/Commons/Settings.qml index d8e8f4bf..8375d67a 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -159,6 +159,10 @@ Singleton { "id": "Tray" }, { "id": "NotificationHistory" + }, { + "id": "WiFi" + }, { + "id": "Bluetooth" }, { "id": "Battery" }, { diff --git a/Modules/ControlCenter/Cards/PowerProfilesCard.qml b/Modules/ControlCenter/Cards/PowerProfilesCard.qml new file mode 100644 index 00000000..d93b26ba --- /dev/null +++ b/Modules/ControlCenter/Cards/PowerProfilesCard.qml @@ -0,0 +1,66 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.UPower +import qs.Commons +import qs.Services +import qs.Widgets + +// Power Profiles: performance, balanced, eco +NBox { + + property real spacing: 0 + + // Centralized service + readonly property bool hasPP: PowerProfileService.available + + RowLayout { + id: powerRow + anchors.fill: parent + anchors.margins: Style.marginS * scaling + spacing: spacing + Item { + Layout.fillWidth: true + } + // Performance + NIconButton { + icon: PowerProfileService.getIcon(PowerProfile.Performance) + tooltipText: I18n.tr("tooltips.set-power-profile", { + "profile": PowerProfileService.getName(PowerProfile.Performance) + }) + enabled: hasPP + opacity: enabled ? Style.opacityFull : Style.opacityMedium + colorBg: (enabled && PowerProfileService.profile === PowerProfile.Performance) ? Color.mPrimary : Color.mSurfaceVariant + colorFg: (enabled && PowerProfileService.profile === PowerProfile.Performance) ? Color.mOnPrimary : Color.mPrimary + onClicked: PowerProfileService.setProfile(PowerProfile.Performance) + } + // Balanced + NIconButton { + icon: PowerProfileService.getIcon(PowerProfile.Balanced) + tooltipText: I18n.tr("tooltips.set-power-profile", { + "profile": PowerProfileService.getName(PowerProfile.Balanced) + }) + enabled: hasPP + opacity: enabled ? Style.opacityFull : Style.opacityMedium + colorBg: (enabled && PowerProfileService.profile === PowerProfile.Balanced) ? Color.mPrimary : Color.mSurfaceVariant + colorFg: (enabled && PowerProfileService.profile === PowerProfile.Balanced) ? Color.mOnPrimary : Color.mPrimary + onClicked: PowerProfileService.setProfile(PowerProfile.Balanced) + } + // Eco + NIconButton { + icon: PowerProfileService.getIcon(PowerProfile.PowerSaver) + tooltipText: I18n.tr("tooltips.set-power-profile", { + "profile": PowerProfileService.getName(PowerProfile.PowerSaver) + }) + enabled: hasPP + opacity: enabled ? Style.opacityFull : Style.opacityMedium + colorBg: (enabled && PowerProfileService.profile === PowerProfile.PowerSaver) ? Color.mPrimary : Color.mSurfaceVariant + colorFg: (enabled && PowerProfileService.profile === PowerProfile.PowerSaver) ? Color.mOnPrimary : Color.mPrimary + onClicked: PowerProfileService.setProfile(PowerProfile.PowerSaver) + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/Modules/ControlCenter/Cards/ProfileCard.qml b/Modules/ControlCenter/Cards/ProfileCard.qml new file mode 100644 index 00000000..ec9b4527 --- /dev/null +++ b/Modules/ControlCenter/Cards/ProfileCard.qml @@ -0,0 +1,113 @@ +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Widgets +import qs.Modules.Settings +import qs.Modules.ControlCenter +import qs.Commons +import qs.Services +import qs.Widgets + +// Header card with avatar, user and quick actions +NBox { + id: root + + property string uptimeText: "--" + + RowLayout { + id: content + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Style.marginM * scaling + spacing: Style.marginM * scaling + + NImageCircled { + width: Style.baseWidgetSize * 1.25 * scaling + height: Style.baseWidgetSize * 1.25 * scaling + imagePath: Settings.data.general.avatarImage + fallbackIcon: "person" + borderColor: Color.mPrimary + borderWidth: Math.max(1, Style.borderM * scaling) + } + + ColumnLayout { + Layout.fillWidth: true + spacing: Style.marginXXS * scaling + NText { + text: Quickshell.env("USER") || "user" + font.weight: Style.fontWeightBold + font.capitalization: Font.Capitalize + } + NText { + text: I18n.tr("system.uptime", { + "uptime": uptimeText + }) + pointSize: Style.fontSizeS * scaling + color: Color.mOnSurfaceVariant + } + } + + RowLayout { + spacing: Style.marginS * scaling + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Item { + Layout.fillWidth: true + } + NIconButton { + icon: "settings" + tooltipText: I18n.tr("tooltips.open-settings") + onClicked: { + settingsPanel.requestedTab = SettingsPanel.Tab.General + settingsPanel.open() + } + } + + NIconButton { + icon: "power" + tooltipText: I18n.tr("tooltips.session-menu") + onClicked: { + sessionMenuPanel.open() + controlCenterPanel.close() + } + } + + NIconButton { + icon: "close" + tooltipText: I18n.tr("tooltips.close") + onClicked: { + controlCenterPanel.close() + } + } + } + } + + // ---------------------------------- + // Uptime + Timer { + interval: 60000 + repeat: true + running: true + onTriggered: uptimeProcess.running = true + } + + Process { + id: uptimeProcess + command: ["cat", "/proc/uptime"] + running: true + + stdout: StdioCollector { + onStreamFinished: { + var uptimeSeconds = parseFloat(this.text.trim().split(' ')[0]) + uptimeText = Time.formatVagueHumanReadableDuration(uptimeSeconds) + uptimeProcess.running = false + } + } + } + + function updateSystemInfo() { + uptimeProcess.running = true + } +} diff --git a/Modules/ControlCenter/Cards/SystemMonitorCard.qml b/Modules/ControlCenter/Cards/SystemMonitorCard.qml index b0572f8a..67ca2c68 100644 --- a/Modules/ControlCenter/Cards/SystemMonitorCard.qml +++ b/Modules/ControlCenter/Cards/SystemMonitorCard.qml @@ -9,10 +9,15 @@ import qs.Widgets NBox { id: root - RowLayout { + ColumnLayout { id: content - anchors.fill: parent - anchors.margins: Style.marginXS * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.leftMargin: Style.marginS * scaling + anchors.rightMargin: Style.marginS * scaling + anchors.topMargin: Style.marginXS * scaling + anchors.bottomMargin: Style.marginM * scaling spacing: Style.marginS * scaling NCircleStat { @@ -20,8 +25,8 @@ NBox { icon: "cpu-usage" flat: true contentScale: 0.8 - Layout.fillWidth: true - Layout.fillHeight: true + width: 72 * scaling + height: 68 * scaling } NCircleStat { value: SystemStatService.cpuTemp @@ -29,24 +34,24 @@ NBox { icon: "cpu-temperature" flat: true contentScale: 0.8 - Layout.fillWidth: true - Layout.fillHeight: true + width: 72 * scaling + height: 68 * scaling } NCircleStat { value: SystemStatService.memPercent icon: "memory" flat: true contentScale: 0.8 - Layout.fillWidth: true - Layout.fillHeight: true + width: 72 * scaling + height: 68 * scaling } NCircleStat { value: SystemStatService.diskPercent icon: "storage" flat: true contentScale: 0.8 - Layout.fillWidth: true - Layout.fillHeight: true + width: 72 * scaling + height: 68 * scaling } } } diff --git a/Modules/ControlCenter/Cards/UtilitiesCard.qml b/Modules/ControlCenter/Cards/UtilitiesCard.qml new file mode 100644 index 00000000..decd9659 --- /dev/null +++ b/Modules/ControlCenter/Cards/UtilitiesCard.qml @@ -0,0 +1,66 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs.Commons +import qs.Modules.Settings +import qs.Services +import qs.Widgets + +// Utilities: record & wallpaper +NBox { + + property real spacing: 0 + + RowLayout { + id: utilRow + anchors.fill: parent + anchors.margins: Style.marginS * scaling + spacing: spacing + Item { + Layout.fillWidth: true + } + // Screen Recorder + NIconButton { + icon: "camera-video" + enabled: ScreenRecorderService.isAvailable + tooltipText: ScreenRecorderService.isAvailable ? (ScreenRecorderService.isRecording ? I18n.tr("tooltips.stop-screen-recording") : I18n.tr("tooltips.start-screen-recording")) : I18n.tr("tooltips.screen-recorder-not-installed") + colorBg: ScreenRecorderService.isRecording ? Color.mPrimary : Color.mSurfaceVariant + colorFg: ScreenRecorderService.isRecording ? Color.mOnPrimary : Color.mPrimary + onClicked: { + if (!ScreenRecorderService.isAvailable) + return + ScreenRecorderService.toggleRecording() + // If we were not recording and we just initiated a start, close the panel + if (!ScreenRecorderService.isRecording) { + var panel = PanelService.getPanel("controlCenterPanel") + panel?.close() + } + } + } + + // Idle Inhibitor + NIconButton { + icon: IdleInhibitorService.isInhibited ? "keep-awake-on" : "keep-awake-off" + tooltipText: IdleInhibitorService.isInhibited ? I18n.tr("tooltips.disable-keep-awake") : I18n.tr("tooltips.enable-keep-awake") + colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant + colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mPrimary + onClicked: { + IdleInhibitorService.manualToggle() + } + } + + // Wallpaper + NIconButton { + visible: Settings.data.wallpaper.enabled + icon: "wallpaper-selector" + tooltipText: I18n.tr("tooltips.wallpaper-selector") + onClicked: PanelService.getPanel("wallpaperPanel")?.toggle(this) + onRightClicked: WallpaperService.setRandomWallpaper() + } + + Item { + Layout.fillWidth: true + } + } +} diff --git a/Modules/ControlCenter/Cards/WeatherCard.qml b/Modules/ControlCenter/Cards/WeatherCard.qml new file mode 100644 index 00000000..8e6257a1 --- /dev/null +++ b/Modules/ControlCenter/Cards/WeatherCard.qml @@ -0,0 +1,130 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.Commons +import qs.Services +import qs.Widgets + +// Weather overview card (placeholder data) +NBox { + id: root + + readonly property bool weatherReady: (LocationService.data.weather !== null) + + ColumnLayout { + id: content + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Style.marginM * scaling + spacing: Style.marginM * scaling + clip: true + + RowLayout { + spacing: Style.marginS * scaling + NIcon { + Layout.alignment: Qt.AlignVCenter + icon: weatherReady ? LocationService.weatherSymbolFromCode(LocationService.data.weather.current_weather.weathercode) : "" + pointSize: Style.fontSizeXXXL * 1.75 * scaling + color: Color.mPrimary + } + + ColumnLayout { + spacing: Style.marginXXS * scaling + NText { + text: { + // Ensure the name is not too long if one had to specify the country + const chunks = Settings.data.location.name.split(",") + return chunks[0] + } + pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + } + + RowLayout { + NText { + visible: weatherReady + text: { + if (!weatherReady) { + return "" + } + var temp = LocationService.data.weather.current_weather.temperature + var suffix = "C" + if (Settings.data.location.useFahrenheit) { + temp = LocationService.celsiusToFahrenheit(temp) + var suffix = "F" + } + temp = Math.round(temp) + return `${temp}°${suffix}` + } + pointSize: Style.fontSizeXL * scaling + font.weight: Style.fontWeightBold + } + + NText { + text: weatherReady ? `(${LocationService.data.weather.timezone_abbreviation})` : "" + pointSize: Style.fontSizeXS * scaling + color: Color.mOnSurfaceVariant + visible: LocationService.data.weather + } + } + } + } + + NDivider { + visible: weatherReady + Layout.fillWidth: true + } + + RowLayout { + visible: weatherReady + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + spacing: Style.marginL * scaling + Repeater { + model: weatherReady ? LocationService.data.weather.daily.time : [] + delegate: ColumnLayout { + Layout.alignment: Qt.AlignHCenter + spacing: Style.marginL * scaling + NText { + text: { + var weatherDate = new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/")) + return Qt.locale().toString(weatherDate, "ddd") + } + color: Color.mOnSurface + Layout.alignment: Qt.AlignHCenter + } + NIcon { + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index]) + pointSize: Style.fontSizeXXL * 1.6 * scaling + color: Color.mPrimary + } + NText { + Layout.alignment: Qt.AlignHCenter + text: { + var max = LocationService.data.weather.daily.temperature_2m_max[index] + var min = LocationService.data.weather.daily.temperature_2m_min[index] + if (Settings.data.location.useFahrenheit) { + max = LocationService.celsiusToFahrenheit(max) + min = LocationService.celsiusToFahrenheit(min) + } + max = Math.round(max) + min = Math.round(min) + return `${max}°/${min}°` + } + pointSize: Style.fontSizeXS * scaling + color: Color.mOnSurfaceVariant + } + } + } + } + + RowLayout { + visible: !weatherReady + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + NBusyIndicator {} + } + } +} diff --git a/Modules/ControlCenter/ControlCenterPanel.qml b/Modules/ControlCenter/ControlCenterPanel.qml index 2d5ba9bf..aa9a8b47 100644 --- a/Modules/ControlCenter/ControlCenterPanel.qml +++ b/Modules/ControlCenter/ControlCenterPanel.qml @@ -10,29 +10,10 @@ import qs.Widgets NPanel { id: root - preferredWidth: 400 - preferredHeight: topHeight + midHeight + bottomHeight + audioHeight + Math.round(Style.marginL * 5) + preferredWidth: 460 + preferredHeight: 734 panelKeyboardFocus: true - readonly property int topHeight: { - const columns = (Settings.data.controlCenter.quickSettingsStyle === "compact") ? 4 : 3 - const rowsCount = Math.ceil(Settings.data.controlCenter.widgets.quickSettings.length / columns) - - var buttonHeight - if (Settings.data.controlCenter.quickSettingsStyle === "classic") { - buttonHeight = Style.baseWidgetSize - } else if (Settings.data.controlCenter.quickSettingsStyle === "compact") { - buttonHeight = Style.baseWidgetSize * 0.8 // Smaller for compact - } else { - buttonHeight = 56 - } - - return (rowsCount * buttonHeight) + 120 - } - readonly property int midHeight: 220 - readonly property int bottomHeight: 80 - readonly property int audioHeight: 120 - // Positioning readonly property string controlCenterPosition: Settings.data.controlCenter.position panelAnchorHorizontalCenter: controlCenterPosition !== "close_to_bar_button" && controlCenterPosition.endsWith("_center") @@ -50,33 +31,60 @@ NPanel { // Layout content ColumnLayout { id: layout - anchors.fill: parent - anchors.margins: content.cardSpacing + x: content.cardSpacing + y: content.cardSpacing + width: parent.width - (2 * content.cardSpacing) spacing: content.cardSpacing - // Top Card: profile + utilities - TopCard { - id: topCard + // Cards (consistent inter-card spacing via ColumnLayout spacing) + ProfileCard { Layout.fillWidth: true - Layout.preferredHeight: topHeight * scaling + Layout.preferredHeight: Math.max(64 * scaling) } - // Audio controls card - AudioCard { + WeatherCard { Layout.fillWidth: true - Layout.preferredHeight: audioHeight * scaling + Layout.preferredHeight: Math.max(220 * scaling) } - // Media card - MediaCard { + // Middle section: media + stats column + RowLayout { Layout.fillWidth: true - Layout.preferredHeight: midHeight * scaling + Layout.preferredHeight: Math.max(310 * scaling) + spacing: content.cardSpacing + + // Media card + MediaCard { + Layout.fillWidth: true + Layout.fillHeight: true + } + + // System monitors combined in one card + SystemMonitorCard { + Layout.preferredWidth: Style.baseWidgetSize * 2.625 * scaling + Layout.fillHeight: true + } } - // System monitors combined in one card - SystemMonitorCard { + // Bottom actions (two grouped rows of round buttons) + RowLayout { Layout.fillWidth: true - Layout.preferredHeight: bottomHeight * scaling + Layout.preferredHeight: Math.max(60 * scaling) + spacing: content.cardSpacing + + // Power Profiles switcher + PowerProfilesCard { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: content.cardSpacing + } + + // Utilities buttons + UtilitiesCard { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: content.cardSpacing + } } } } diff --git a/Modules/Settings/SettingsPanel.qml b/Modules/Settings/SettingsPanel.qml index cb76dff4..28b21800 100644 --- a/Modules/Settings/SettingsPanel.qml +++ b/Modules/Settings/SettingsPanel.qml @@ -129,12 +129,14 @@ NPanel { "label": "settings.bar.title", "icon": "settings-bar", "source": barTab - }, { - "id": SettingsPanel.Tab.ControlCenter, - "label": "settings.control-center.title", - "icon": "settings-bar", - "source": controlCenterTab - }, { + }, + //{ + // "id": SettingsPanel.Tab.ControlCenter, + // "label": "settings.control-center.title", + // "icon": "settings-bar", + // "source": controlCenterTab + //}, + { "id": SettingsPanel.Tab.Dock, "label": "settings.dock.title", "icon": "settings-dock", diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index cdefd487..e1110eb1 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -8,8 +8,8 @@ Singleton { id: root // Public properties - property string baseVersion: "2.17.1" - property bool isDevelopment: true + property string baseVersion: "2.17.2" + property bool isDevelopment: false property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}`