diff --git a/Assets/Fonts/tabler/tabler-icons.ttf b/Assets/Fonts/tabler/tabler-icons.ttf index 8626fa1..2d936ba 100644 Binary files a/Assets/Fonts/tabler/tabler-icons.ttf and b/Assets/Fonts/tabler/tabler-icons.ttf differ diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 7d3d885..a6ecf64 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -121,6 +121,12 @@ Singleton { } } } + + // Upgrade the density of the bar so the look stay the same for people who upgrade. + if (adapter.settingsVersion == 2) { + adapter.bar.density = "comfortable" + adapter.settingsVersion++ + } } // ----------------------------------------------------- @@ -259,13 +265,15 @@ Singleton { JsonAdapter { id: adapter - property int settingsVersion: 2 + property int settingsVersion: 3 // bar property JsonObject bar: JsonObject { property string position: "top" // "top", "bottom", "left", or "right" property real backgroundOpacity: 1.0 property list monitors: [] + property string density: "default" // "compact", "default", "comfortable" + property bool showCapsule: true // Floating bar settings property bool floating: false @@ -320,7 +328,7 @@ Singleton { // general property JsonObject general: JsonObject { property string avatarImage: defaultAvatar - property bool dimDesktop: false + property bool dimDesktop: true property bool showScreenCorners: false property bool forceBlackScreenCorners: false property real radiusRatio: 1.0 diff --git a/Commons/Style.qml b/Commons/Style.qml index 99d88e6..2136e2e 100644 --- a/Commons/Style.qml +++ b/Commons/Style.qml @@ -65,14 +65,36 @@ Singleton { property int animationSlow: Math.round(450 / Settings.data.general.animationSpeed) property int animationSlowest: Math.round(750 / Settings.data.general.animationSpeed) - // Dimensions - property int barHeight: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? 39 : 37 - property int capsuleHeight: (barHeight * 0.73) - property int baseWidgetSize: (barHeight * 0.9) - property int sliderWidth: 200 - // Delays property int tooltipDelay: 300 property int tooltipDelayLong: 1200 property int pillDelay: 500 + + // Settings widgets base size + property real baseWidgetSize: 33 + property real sliderWidth: 200 + + // Bar Dimensions + property real barHeight: { + if (Settings.data.bar.density === "compact") { + return (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? 27 : 25 + } + if (Settings.data.bar.density === "default") { + return (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? 33 : 31 + } + if (Settings.data.bar.density === "comfortable") { + return (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? 39 : 37 + } + } + property real capsuleHeight: { + if (Settings.data.bar.density === "compact") { + return barHeight * 0.85 + } + if (Settings.data.bar.density === "default") { + return barHeight * 0.82 + } + if (Settings.data.bar.density === "comfortable") { + return barHeight * 0.73 + } + } } diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 11a618d..c98798f 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -68,23 +68,12 @@ Variants { radius: Settings.data.bar.floating ? Style.radiusL : 0 } - // For vertical bars, use a single column layout Loader { - id: verticalBarLayout anchors.fill: parent - visible: Settings.data.bar.position === "left" || Settings.data.bar.position === "right" - sourceComponent: verticalBarComponent + sourceComponent: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? verticalBarComponent : horizontalBarComponent } - // For horizontal bars, use the original three-section layout - Loader { - id: horizontalBarLayout - anchors.fill: parent - visible: Settings.data.bar.position === "top" || Settings.data.bar.position === "bottom" - sourceComponent: horizontalBarComponent - } - - // Main layout components + // For vertical bars Component { id: verticalBarComponent Item { @@ -163,6 +152,7 @@ Variants { } } + // For horizontal bars Component { id: horizontalBarComponent Item { diff --git a/Widgets/NPill.qml b/Modules/Bar/Extras/BarPill.qml similarity index 94% rename from Widgets/NPill.qml rename to Modules/Bar/Extras/BarPill.qml index d6f0fac..6714d17 100644 --- a/Widgets/NPill.qml +++ b/Modules/Bar/Extras/BarPill.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import qs.Commons import qs.Services +import qs.Widgets Item { id: root @@ -10,13 +11,13 @@ Item { property string text: "" property string suffix: "" property string tooltipText: "" - property real sizeRatio: 0.8 property bool autoHide: false property bool forceOpen: false property bool forceClose: false property bool disableOpen: false property bool rightOpen: false property bool hovered: false + property bool compact: false readonly property string barPosition: Settings.data.bar.position readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right" @@ -41,18 +42,18 @@ Item { Component { id: verticalPillComponent - NPillVertical { + BarPillVertical { icon: root.icon text: root.text suffix: root.suffix tooltipText: root.tooltipText - sizeRatio: root.sizeRatio autoHide: root.autoHide forceOpen: root.forceOpen forceClose: root.forceClose disableOpen: root.disableOpen rightOpen: root.rightOpen hovered: root.hovered + compact: root.compact onShown: root.shown() onHidden: root.hidden() onEntered: root.entered() @@ -66,18 +67,18 @@ Item { Component { id: horizontalPillComponent - NPillHorizontal { + BarPillHorizontal { icon: root.icon text: root.text suffix: root.suffix tooltipText: root.tooltipText - sizeRatio: root.sizeRatio autoHide: root.autoHide forceOpen: root.forceOpen forceClose: root.forceClose disableOpen: root.disableOpen rightOpen: root.rightOpen hovered: root.hovered + compact: root.compact onShown: root.shown() onHidden: root.hidden() onEntered: root.entered() diff --git a/Widgets/NPillHorizontal.qml b/Modules/Bar/Extras/BarPillHorizontal.qml similarity index 85% rename from Widgets/NPillHorizontal.qml rename to Modules/Bar/Extras/BarPillHorizontal.qml index 89888ba..0e4b548 100644 --- a/Widgets/NPillHorizontal.qml +++ b/Modules/Bar/Extras/BarPillHorizontal.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import qs.Commons import qs.Services +import qs.Widgets Item { id: root @@ -10,13 +11,13 @@ Item { property string text: "" property string suffix: "" property string tooltipText: "" - property real sizeRatio: 0.8 property bool autoHide: false property bool forceOpen: false property bool forceClose: false property bool disableOpen: false property bool rightOpen: false property bool hovered: false + property bool compact: false // Effective shown state (true if hovered/animated open or forced) readonly property bool revealed: forceOpen || showPill @@ -34,26 +35,27 @@ Item { property bool showPill: false property bool shouldAnimateHide: false - // Exposed width logic - readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling) - readonly property int pillHeight: iconSize - readonly property int pillPaddingHorizontal: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin - readonly property int pillOverlap: iconSize * 0.5 - readonly property int maxPillWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap) + readonly property int pillHeight: Math.round(Style.capsuleHeight * scaling) + readonly property int pillPaddingHorizontal: Math.round(Style.capsuleHeight * 0.2 * scaling) + readonly property int pillOverlap: Math.round(Style.capsuleHeight * 0.5 * scaling) + readonly property int pillMaxWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap) - width: iconSize + Math.max(0, pill.width - pillOverlap) + readonly property real iconSize: Math.max(1, compact ? pillHeight * 0.65 : pillHeight * 0.48) + readonly property real textSize: Math.max(1, compact ? pillHeight * 0.45 : pillHeight * 0.33) + + width: pillHeight + Math.max(0, pill.width - pillOverlap) height: pillHeight Rectangle { id: pill - width: revealed ? maxPillWidth : 1 + width: revealed ? pillMaxWidth : 1 height: pillHeight x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right (iconCircle.x + iconCircle.width / 2) - width // Opens left opacity: revealed ? Style.opacityFull : Style.opacityNone - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent topLeftRadius: rightOpen ? 0 : pillHeight * 0.5 bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5 @@ -76,7 +78,7 @@ Item { } text: root.text + root.suffix font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXS * scaling + font.pointSize: textSize font.weight: Style.fontWeightBold color: forceOpen ? Color.mOnSurface : Color.mPrimary visible: revealed @@ -100,10 +102,10 @@ Item { Rectangle { id: iconCircle - width: iconSize - height: iconSize + width: pillHeight + height: pillHeight radius: width * 0.5 - color: hovered ? Color.mTertiary : Color.mSurfaceVariant + color: hovered ? Color.mTertiary : Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent anchors.verticalCenter: parent.verticalCenter x: rightOpen ? 0 : (parent.width - width) @@ -117,7 +119,7 @@ Item { NIcon { icon: root.icon - font.pointSize: Style.fontSizeM * scaling + font.pointSize: iconSize color: hovered ? Color.mOnTertiary : Color.mOnSurface // Center horizontally x: (iconCircle.width - width) / 2 @@ -133,7 +135,7 @@ Item { target: pill property: "width" from: 1 - to: maxPillWidth + to: pillMaxWidth duration: Style.animationNormal easing.type: Easing.OutCubic } @@ -173,7 +175,7 @@ Item { NumberAnimation { target: pill property: "width" - from: maxPillWidth + from: pillMaxWidth to: 1 duration: Style.animationNormal easing.type: Easing.InCubic diff --git a/Widgets/NPillVertical.qml b/Modules/Bar/Extras/BarPillVertical.qml similarity index 87% rename from Widgets/NPillVertical.qml rename to Modules/Bar/Extras/BarPillVertical.qml index fb5037a..494083e 100644 --- a/Widgets/NPillVertical.qml +++ b/Modules/Bar/Extras/BarPillVertical.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import qs.Commons import qs.Services +import qs.Widgets Item { id: root @@ -10,13 +11,13 @@ Item { property string text: "" property string suffix: "" property string tooltipText: "" - property real sizeRatio: 0.8 property bool autoHide: false property bool forceOpen: false property bool forceClose: false property bool disableOpen: false property bool rightOpen: false property bool hovered: false + property bool compact: false // Bar position detection for pill direction readonly property string barPosition: Settings.data.bar.position @@ -43,16 +44,19 @@ Item { property bool shouldAnimateHide: false // Sizing logic for vertical bars - readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling) - readonly property int pillHeight: iconSize + readonly property int buttonSize: Math.round(Style.capsuleHeight * scaling) + readonly property int pillHeight: buttonSize readonly property int pillPaddingVertical: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin - readonly property int pillOverlap: iconSize * 0.5 - readonly property int maxPillWidth: iconSize + readonly property int pillOverlap: buttonSize * 0.5 + readonly property int maxPillWidth: buttonSize readonly property int maxPillHeight: Math.max(1, textItem.implicitHeight + pillPaddingVertical * 4) + readonly property real iconSize: Math.max(1, compact ? pillHeight * 0.65 : pillHeight * 0.48) + readonly property real textSize: Math.max(1, compact ? pillHeight * 0.38 : pillHeight * 0.33) + // For vertical bars: width is just icon size, height includes pill space - width: iconSize - height: revealed ? (iconSize + maxPillHeight - pillOverlap) : iconSize + width: buttonSize + height: revealed ? (buttonSize + maxPillHeight - pillOverlap) : buttonSize Rectangle { id: pill @@ -64,13 +68,13 @@ Item { y: openUpward ? (iconCircle.y + iconCircle.height / 2 - height) : (iconCircle.y + iconCircle.height / 2) opacity: revealed ? Style.opacityFull : Style.opacityNone - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent // Radius logic for vertical expansion - rounded on the side that connects to icon - topLeftRadius: openUpward ? iconSize * 0.5 : 0 - bottomLeftRadius: openDownward ? iconSize * 0.5 : 0 - topRightRadius: openUpward ? iconSize * 0.5 : 0 - bottomRightRadius: openDownward ? iconSize * 0.5 : 0 + topLeftRadius: openUpward ? buttonSize * 0.5 : 0 + bottomLeftRadius: openDownward ? buttonSize * 0.5 : 0 + topRightRadius: openUpward ? buttonSize * 0.5 : 0 + bottomRightRadius: openDownward ? buttonSize * 0.5 : 0 anchors.horizontalCenter: parent.horizontalCenter @@ -88,7 +92,7 @@ Item { } text: root.text + root.suffix font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXXS * scaling + font.pointSize: textSize font.weight: Style.fontWeightMedium horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -121,10 +125,10 @@ Item { Rectangle { id: iconCircle - width: iconSize - height: iconSize + width: buttonSize + height: buttonSize radius: width * 0.5 - color: hovered ? Color.mTertiary : Color.mSurfaceVariant + color: hovered ? Color.mTertiary : Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent // Icon positioning based on direction x: 0 @@ -140,7 +144,7 @@ Item { NIcon { icon: root.icon - font.pointSize: Style.fontSizeM * scaling + font.pointSize: iconSize color: hovered ? Color.mOnTertiary : Color.mOnSurface // Center horizontally x: (iconCircle.width - width) / 2 diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index e9bb66d..c190445 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -37,8 +37,18 @@ Item { readonly property real maxWidth: minWidth * 2 readonly property string barPosition: Settings.data.bar.position + readonly property bool isVertical: barPosition === "left" || barPosition === "right" + readonly property bool compact: (Settings.data.bar.density === "compact") + implicitHeight: (barPosition === "left" || barPosition === "right") ? calculatedVerticalHeight() : Math.round(Style.barHeight * scaling) - implicitWidth: (barPosition === "left" || barPosition === "right") ? Math.round(Style.baseWidgetSize * 0.8 * scaling) : (horizontalLayout.implicitWidth + Style.marginM * 2 * scaling) + implicitWidth: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * 0.8 * scaling) : (horizontalLayout.implicitWidth + Style.marginM * 2 * scaling) + + readonly property real textSize: { + var base = isVertical ? width : height + return Math.max(1, compact ? base * 0.43 : base * 0.33) + } + + readonly property real iconSize: textSize * 1.25 function getTitle() { try { @@ -60,7 +70,7 @@ Item { let total = Style.marginM * 2 * scaling // internal padding if (showIcon) { - total += Style.baseWidgetSize * 0.5 * scaling + 2 * scaling // icon + spacing + total += Style.capsuleHeight * 0.5 * scaling + 2 * scaling // icon + spacing } // Calculate actual text width more accurately @@ -129,12 +139,14 @@ Item { Rectangle { id: windowTitleRect visible: root.visible - anchors.left: parent.left + anchors.left: (barPosition === "top" || barPosition === "bottom") ? parent.left : undefined + anchors.top: (barPosition === "left" || barPosition === "right") ? parent.top : undefined anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter width: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : (horizontalLayout.implicitWidth + Style.marginM * 2 * scaling) height: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : Math.round(Style.capsuleHeight * scaling) - radius: Math.round(Style.radiusM * scaling) - color: Color.mSurfaceVariant + radius: width / 2 + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent Item { id: mainContainer @@ -152,8 +164,8 @@ Item { // Window icon Item { - Layout.preferredWidth: Style.baseWidgetSize * 0.5 * scaling - Layout.preferredHeight: Style.baseWidgetSize * 0.5 * scaling + Layout.preferredWidth: Style.capsuleHeight * 0.75 * scaling + Layout.preferredHeight: Style.capsuleHeight * 0.75 * scaling Layout.alignment: Qt.AlignVCenter visible: getTitle() !== "" && showIcon @@ -217,10 +229,9 @@ Item { // Window icon Item { - width: Style.baseWidgetSize * 0.5 * scaling - height: Style.baseWidgetSize * 0.5 * scaling + width: Style.capsuleHeight * 0.75 * scaling + height: Style.capsuleHeight * 0.75 * scaling anchors.centerIn: parent - visible: getTitle() !== "" && showIcon IconImage { id: windowIconVertical diff --git a/Modules/Bar/Widgets/Battery.qml b/Modules/Bar/Widgets/Battery.qml index fa2705b..2b5ec67 100644 --- a/Modules/Bar/Widgets/Battery.qml +++ b/Modules/Bar/Widgets/Battery.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import qs.Commons import qs.Services import qs.Widgets +import qs.Modules.Bar.Extras Item { id: root @@ -81,10 +82,11 @@ Item { } } - NPill { + BarPill { id: pill - rightOpen: BarWidgetRegistry.getNPillDirection(root) + compact: (Settings.data.bar.density === "compact") + rightOpen: BarWidgetRegistry.getPillDirection(root) icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent, charging, isReady) text: (isReady || testMode) ? Math.round(percent) : "-" suffix: "%" diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index 41588bf..656794b 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -13,8 +13,9 @@ NIconButton { property ShellScreen screen property real scaling: 1.0 - sizeRatio: 0.8 - colorBg: Color.mSurfaceVariant + baseSize: Style.capsuleHeight + compact: (Settings.data.bar.density === "compact") + colorBg: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/Brightness.qml b/Modules/Bar/Widgets/Brightness.qml index 0ca072e..c588b1d 100644 --- a/Modules/Bar/Widgets/Brightness.qml +++ b/Modules/Bar/Widgets/Brightness.qml @@ -4,6 +4,7 @@ import qs.Commons import qs.Modules.SettingsPanel import qs.Services import qs.Widgets +import qs.Modules.Bar.Extras Item { id: root @@ -73,10 +74,11 @@ Item { onTriggered: pill.hide() } - NPill { + BarPill { id: pill - rightOpen: BarWidgetRegistry.getNPillDirection(root) + compact: (Settings.data.bar.density === "compact") + rightOpen: BarWidgetRegistry.getPillDirection(root) icon: getIcon() autoHide: false // Important to be false so we can hover as long as we want text: { diff --git a/Modules/Bar/Widgets/Clock.qml b/Modules/Bar/Widgets/Clock.qml index 45ebece..3b2e4f7 100644 --- a/Modules/Bar/Widgets/Clock.qml +++ b/Modules/Bar/Widgets/Clock.qml @@ -29,6 +29,7 @@ Rectangle { } readonly property string barPosition: Settings.data.bar.position + readonly property bool compact: (Settings.data.bar.density === "compact") // Resolve settings: try user settings or defaults from BarWidgetRegistry readonly property bool use12h: widgetSettings.use12HourClock !== undefined ? widgetSettings.use12HourClock : widgetMetadata.use12HourClock @@ -39,15 +40,15 @@ Rectangle { readonly property bool verticalMode: barPosition === "left" || barPosition === "right" implicitWidth: verticalMode ? Math.round(Style.capsuleHeight * scaling) : Math.round(layout.implicitWidth + Style.marginM * 2 * scaling) - implicitHeight: verticalMode ? Math.round(Style.capsuleHeight * 2.5 * scaling) : Math.round(Style.baseWidgetSize * 0.8 * scaling) // Match NPill + implicitHeight: verticalMode ? Math.round(Style.capsuleHeight * 2.5 * scaling) : Math.round(Style.capsuleHeight * scaling) // Match BarPill radius: Math.round(Style.radiusS * scaling) - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent Item { id: clockContainer anchors.fill: parent - anchors.margins: Style.marginXS * scaling + anchors.margins: compact ? 0 : Style.marginXS * scaling ColumnLayout { id: layout diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml index 9ab1b11..496c670 100644 --- a/Modules/Bar/Widgets/CustomButton.qml +++ b/Modules/Bar/Widgets/CustomButton.qml @@ -6,6 +6,7 @@ import qs.Commons import qs.Services import qs.Widgets import qs.Modules.SettingsPanel +import qs.Modules.Bar.Extras Item { id: root @@ -43,12 +44,13 @@ Item { implicitWidth: pill.width implicitHeight: pill.height - NPill { + BarPill { id: pill - rightOpen: BarWidgetRegistry.getNPillDirection(root) + rightOpen: BarWidgetRegistry.getPillDirection(root) icon: customIcon text: _dynamicText + compact: (Settings.data.bar.density === "compact") autoHide: false forceOpen: _dynamicText !== "" forceClose: false diff --git a/Modules/Bar/Widgets/DarkModeToggle.qml b/Modules/Bar/Widgets/DarkModeToggle.qml index cf68958..a5ada46 100644 --- a/Modules/Bar/Widgets/DarkModeToggle.qml +++ b/Modules/Bar/Widgets/DarkModeToggle.qml @@ -10,10 +10,10 @@ NIconButton { property real scaling: 1.0 icon: "dark-mode" - tooltipText: "Toggle light/dark mode" - sizeRatio: 0.8 - - colorBg: Settings.data.colorSchemes.darkMode ? Color.mSurfaceVariant : Color.mPrimary + tooltipText: "Toggle light/dark mode." + compact: (Settings.data.bar.density === "compact") + baseSize: Style.capsuleHeight + colorBg: Settings.data.colorSchemes.darkMode ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/KeepAwake.qml b/Modules/Bar/Widgets/KeepAwake.qml index 10a55f2..9c4626f 100644 --- a/Modules/Bar/Widgets/KeepAwake.qml +++ b/Modules/Bar/Widgets/KeepAwake.qml @@ -11,14 +11,12 @@ NIconButton { property ShellScreen screen property real scaling: 1.0 - sizeRatio: 0.8 - + baseSize: Style.capsuleHeight + compact: (Settings.data.bar.density === "compact") icon: IdleInhibitorService.isInhibited ? "keep-awake-on" : "keep-awake-off" tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake" : "Enable keep awake" - colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant + colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface colorBorder: Color.transparent - onClicked: { - IdleInhibitorService.manualToggle() - } + onClicked: IdleInhibitorService.manualToggle() } diff --git a/Modules/Bar/Widgets/KeyboardLayout.qml b/Modules/Bar/Widgets/KeyboardLayout.qml index 87aef27..13b4dac 100644 --- a/Modules/Bar/Widgets/KeyboardLayout.qml +++ b/Modules/Bar/Widgets/KeyboardLayout.qml @@ -6,6 +6,7 @@ import Quickshell.Io import qs.Commons import qs.Services import qs.Widgets +import qs.Modules.Bar.Extras Item { id: root @@ -38,11 +39,12 @@ Item { implicitWidth: pill.width implicitHeight: pill.height - NPill { + BarPill { id: pill anchors.verticalCenter: parent.verticalCenter - rightOpen: BarWidgetRegistry.getNPillDirection(root) + compact: (Settings.data.bar.density === "compact") + rightOpen: BarWidgetRegistry.getPillDirection(root) icon: "keyboard" autoHide: false // Important to be false so we can hover as long as we want text: currentLayout.toUpperCase() diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml index 5cdf228..6d55d48 100644 --- a/Modules/Bar/Widgets/MediaMini.qml +++ b/Modules/Bar/Widgets/MediaMini.qml @@ -31,6 +31,7 @@ Item { } readonly property string barPosition: Settings.data.bar.position + readonly property bool compact: (Settings.data.bar.density === "compact") readonly property bool showAlbumArt: (widgetSettings.showAlbumArt !== undefined) ? widgetSettings.showAlbumArt : widgetMetadata.showAlbumArt readonly property bool showVisualizer: (widgetSettings.showVisualizer !== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer @@ -81,7 +82,7 @@ Item { width: (barPosition === "left" || barPosition === "right") ? Math.round(Style.baseWidgetSize * 0.8 * scaling) : (rowLayout.implicitWidth + Style.marginM * 2 * scaling) height: (barPosition === "left" || barPosition === "right") ? Math.round(Style.baseWidgetSize * 0.8 * scaling) : Math.round(Style.capsuleHeight * scaling) radius: (barPosition === "left" || barPosition === "right") ? width / 2 : Math.round(Style.radiusM * scaling) - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent // Used to anchor the tooltip, so the tooltip does not move when the content expands Item { diff --git a/Modules/Bar/Widgets/Microphone.qml b/Modules/Bar/Widgets/Microphone.qml index 3844260..7504414 100644 --- a/Modules/Bar/Widgets/Microphone.qml +++ b/Modules/Bar/Widgets/Microphone.qml @@ -6,6 +6,7 @@ import qs.Commons import qs.Modules.SettingsPanel import qs.Services import qs.Widgets +import qs.Modules.Bar.Extras Item { id: root @@ -86,10 +87,11 @@ Item { } } - NPill { + BarPill { id: pill - rightOpen: BarWidgetRegistry.getNPillDirection(root) + rightOpen: BarWidgetRegistry.getPillDirection(root) icon: getIcon() + compact: (Settings.data.bar.density === "compact") autoHide: false // Important to be false so we can hover as long as we want text: Math.floor(AudioService.inputVolume * 100) suffix: "%" diff --git a/Modules/Bar/Widgets/NightLight.qml b/Modules/Bar/Widgets/NightLight.qml index 43028a1..d48097c 100644 --- a/Modules/Bar/Widgets/NightLight.qml +++ b/Modules/Bar/Widgets/NightLight.qml @@ -14,8 +14,9 @@ NIconButton { property ShellScreen screen property real scaling: 1.0 - sizeRatio: 0.8 - colorBg: Settings.data.nightLight.forced ? Color.mPrimary : Color.mSurfaceVariant + compact: (Settings.data.bar.density === "compact") + baseSize: Style.capsuleHeight + colorBg: Settings.data.nightLight.forced ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Settings.data.nightLight.forced ? Color.mOnPrimary : Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/NotificationHistory.qml b/Modules/Bar/Widgets/NotificationHistory.qml index 3021b47..91faa68 100644 --- a/Modules/Bar/Widgets/NotificationHistory.qml +++ b/Modules/Bar/Widgets/NotificationHistory.qml @@ -49,10 +49,11 @@ NIconButton { return count } - sizeRatio: 0.8 + baseSize: Style.capsuleHeight + compact: (Settings.data.bar.density === "compact") icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell" tooltipText: Settings.data.notifications.doNotDisturb ? "Notification history.\nRight-click to disable 'Do Not Disturb'." : "Notification history.\nRight-click to enable 'Do Not Disturb'." - colorBg: Color.mSurfaceVariant + colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent @@ -68,29 +69,20 @@ NIconButton { Loader { anchors.right: parent.right anchors.top: parent.top - anchors.rightMargin: -4 * scaling - anchors.topMargin: -4 * scaling + anchors.rightMargin: 2 * scaling + anchors.topMargin: 1 * scaling z: 2 active: showUnreadBadge && (!hideWhenZero || computeUnreadCount() > 0) sourceComponent: Rectangle { id: badge readonly property int count: computeUnreadCount() - readonly property string label: count <= 99 ? String(count) : "99+" - readonly property real pad: 8 * scaling - height: 16 * scaling - width: Math.max(height, textNode.implicitWidth + pad) + height: 8 * scaling + width: height radius: height / 2 color: Color.mError border.color: Color.mSurface border.width: 1 visible: count > 0 || !hideWhenZero - NText { - id: textNode - anchors.centerIn: parent - text: badge.label - font.pointSize: Style.fontSizeXXS * scaling - color: Color.mOnError - } } } } diff --git a/Modules/Bar/Widgets/PowerProfile.qml b/Modules/Bar/Widgets/PowerProfile.qml index 219e907..2ddda3f 100644 --- a/Modules/Bar/Widgets/PowerProfile.qml +++ b/Modules/Bar/Widgets/PowerProfile.qml @@ -13,7 +13,7 @@ NIconButton { property real scaling: 1.0 readonly property bool hasPP: PowerProfileService.available - sizeRatio: 0.8 + baseSize: Style.capsuleHeight visible: hasPP function profileIcon() { @@ -46,7 +46,8 @@ NIconButton { icon: root.profileIcon() tooltipText: root.profileName() - colorBg: (PowerProfileService.profile === PowerProfile.Balanced) ? Color.mSurfaceVariant : Color.mPrimary + compact: (Settings.data.bar.density === "compact") + colorBg: (PowerProfileService.profile === PowerProfile.Balanced) ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary colorFg: (PowerProfileService.profile === PowerProfile.Balanced) ? Color.mOnSurface : Color.mOnPrimary colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/PowerToggle.qml b/Modules/Bar/Widgets/PowerToggle.qml index eccecf3..9f33274 100644 --- a/Modules/Bar/Widgets/PowerToggle.qml +++ b/Modules/Bar/Widgets/PowerToggle.qml @@ -11,11 +11,11 @@ NIconButton { property ShellScreen screen property real scaling: 1.0 - sizeRatio: 0.8 - + compact: (Settings.data.bar.density === "compact") + baseSize: Style.capsuleHeight icon: "power" tooltipText: "Power Settings" - colorBg: Color.mSurfaceVariant + colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mError colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/ScreenRecorderIndicator.qml b/Modules/Bar/Widgets/ScreenRecorderIndicator.qml index efa8299..e8a36cb 100644 --- a/Modules/Bar/Widgets/ScreenRecorderIndicator.qml +++ b/Modules/Bar/Widgets/ScreenRecorderIndicator.qml @@ -12,8 +12,9 @@ NIconButton { visible: ScreenRecorderService.isRecording icon: "camera-video" - tooltipText: "Screen recording is active\nClick to stop recording" - sizeRatio: 0.8 + tooltipText: "Screen recording is active.\nClick to stop recording." + compact: (Settings.data.bar.density === "compact") + baseSize: Style.capsuleHeight colorBg: Color.mPrimary colorFg: Color.mOnPrimary onClicked: ScreenRecorderService.toggleRecording() diff --git a/Modules/Bar/Widgets/SidePanelToggle.qml b/Modules/Bar/Widgets/SidePanelToggle.qml index c3347a8..b52e41f 100644 --- a/Modules/Bar/Widgets/SidePanelToggle.qml +++ b/Modules/Bar/Widgets/SidePanelToggle.qml @@ -33,9 +33,9 @@ NIconButton { icon: useDistroLogo ? "" : "noctalia" tooltipText: "Open side panel." - sizeRatio: 0.85 - - colorBg: Color.mSurfaceVariant + baseSize: Style.capsuleHeight + compact: (Settings.data.bar.density === "compact") + colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mOnSurface colorBgHover: useDistroLogo ? Color.mSurfaceVariant : Color.mTertiary colorBorder: Color.transparent @@ -46,10 +46,11 @@ NIconButton { IconImage { id: logo anchors.centerIn: parent - width: root.width * 0.85 + width: root.width * 0.8 height: width source: useDistroLogo ? DistroLogoService.osLogo : "" visible: useDistroLogo && source !== "" smooth: true + asynchronous: true } } diff --git a/Modules/Bar/Widgets/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml index 37cdc42..5bc5f86 100644 --- a/Modules/Bar/Widgets/SystemMonitor.qml +++ b/Modules/Bar/Widgets/SystemMonitor.qml @@ -29,6 +29,8 @@ Rectangle { } readonly property string barPosition: Settings.data.bar.position + readonly property bool isVertical: barPosition === "left" || barPosition === "right" + readonly property bool compact: (Settings.data.bar.density === "compact") readonly property bool showCpuUsage: (widgetSettings.showCpuUsage !== undefined) ? widgetSettings.showCpuUsage : widgetMetadata.showCpuUsage readonly property bool showCpuTemp: (widgetSettings.showCpuTemp !== undefined) ? widgetSettings.showCpuTemp : widgetMetadata.showCpuTemp @@ -37,410 +39,267 @@ Rectangle { readonly property bool showNetworkStats: (widgetSettings.showNetworkStats !== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats readonly property bool showDiskUsage: (widgetSettings.showDiskUsage !== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage + readonly property real textSize: { + var base = isVertical ? width * 0.82 : height + return Math.max(1, compact ? base * 0.43 : base * 0.33) + } + + readonly property real iconSize: textSize * 1.25 + anchors.centerIn: parent - implicitWidth: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : Math.round(horizontalLayout.implicitWidth + Style.marginM * 2 * scaling) - implicitHeight: (barPosition === "left" || barPosition === "right") ? Math.round(verticalLayout.implicitHeight + Style.marginM * 2 * scaling) : Math.round(Style.capsuleHeight * scaling) + implicitWidth: isVertical ? Math.round(Style.capsuleHeight * scaling) : Math.round(mainGrid.implicitWidth + Style.marginM * 2 * scaling) + implicitHeight: isVertical ? Math.round(mainGrid.implicitHeight + Style.marginM * 2 * scaling) : Math.round(Style.capsuleHeight * scaling) radius: Math.round(Style.radiusM * scaling) - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent - // Compact speed formatter for vertical bar display - function formatCompactSpeed(bytesPerSecond) { - if (!bytesPerSecond || bytesPerSecond <= 0) - return "0" - const units = ["", "k", "M", "G"] - let value = bytesPerSecond - let unitIndex = 0 - while (value >= 1024 && unitIndex < units.length - 1) { - value = value / 1024.0 - unitIndex++ - } - // Promote at ~100 of current unit (e.g., 100k -> ~0.1M shown as 0.1M or 0M if rounded) - if (unitIndex < units.length - 1 && value >= 100) { - value = value / 1024.0 - unitIndex++ - } - const display = (value >= 10) ? Math.round(value).toString() : value.toFixed(1) - return display + units[unitIndex] - } - - // Horizontal layout for top/bottom bars - RowLayout { - id: horizontalLayout + GridLayout { + id: mainGrid anchors.centerIn: parent - anchors.leftMargin: Style.marginM * scaling - anchors.rightMargin: Style.marginM * scaling - spacing: Style.marginXS * scaling - visible: barPosition === "top" || barPosition === "bottom" + + // Dynamic layout based on bar orientation + flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: isVertical ? -1 : 1 + columns: isVertical ? 1 : -1 + + rowSpacing: isVertical ? (Style.marginS * scaling) : (Style.marginXS * scaling) + columnSpacing: isVertical ? (Style.marginXS * scaling) : (Style.marginXS * scaling) // CPU Usage Component Item { - Layout.preferredWidth: cpuUsageRow.implicitWidth + Layout.preferredWidth: cpuUsageContent.implicitWidth Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.alignment: Qt.AlignVCenter + Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showCpuUsage - RowLayout { - id: cpuUsageRow + GridLayout { + id: cpuUsageContent anchors.centerIn: parent - spacing: Style.marginXXS * scaling + flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: isVertical ? 2 : 1 + columns: isVertical ? 1 : 2 + rowSpacing: Style.marginXXS * scaling + columnSpacing: Style.marginXXS * scaling + + NText { + text: isVertical ? `${Math.round(SystemStatService.cpuUsage)}%` : `${SystemStatService.cpuUsage}%` + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + } NIcon { icon: "cpu-usage" - font.pointSize: Style.fontSizeM * scaling - Layout.alignment: Qt.AlignVCenter - } - - NText { - text: `${SystemStatService.cpuUsage}%` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + font.pointSize: iconSize + Layout.alignment: Qt.AlignCenter + Layout.row: isVertical ? 1 : 0 + Layout.column: 0 } } } // CPU Temperature Component Item { - Layout.preferredWidth: cpuTempRow.implicitWidth + Layout.preferredWidth: cpuTempContent.implicitWidth Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.alignment: Qt.AlignVCenter + Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showCpuTemp - RowLayout { - id: cpuTempRow + GridLayout { + id: cpuTempContent anchors.centerIn: parent - spacing: Style.marginXXS * scaling + flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: isVertical ? 2 : 1 + columns: isVertical ? 1 : 2 + rowSpacing: Style.marginXXS * scaling + columnSpacing: Style.marginXXS * scaling + + NText { + text: isVertical ? `${SystemStatService.cpuTemp}°` : `${SystemStatService.cpuTemp}°C` + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + } NIcon { icon: "cpu-temperature" - // Fire is so tall, we need to make it smaller - font.pointSize: Style.fontSizeS * scaling - Layout.alignment: Qt.AlignVCenter - } - - NText { - text: `${SystemStatService.cpuTemp}°C` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + font.pointSize: iconSize + Layout.alignment: Qt.AlignCenter + Layout.row: isVertical ? 1 : 0 + Layout.column: 0 } } } // Memory Usage Component Item { - Layout.preferredWidth: memoryUsageRow.implicitWidth + Layout.preferredWidth: memoryContent.implicitWidth Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.alignment: Qt.AlignVCenter + Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showMemoryUsage - RowLayout { - id: memoryUsageRow + GridLayout { + id: memoryContent anchors.centerIn: parent - spacing: Style.marginXXS * scaling + flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: isVertical ? 2 : 1 + columns: isVertical ? 1 : 2 + rowSpacing: Style.marginXXS * scaling + columnSpacing: Style.marginXXS * scaling + + NText { + text: { + if (showMemoryAsPercent) { + return `${SystemStatService.memPercent}%` + } else { + return isVertical ? `${Math.round(SystemStatService.memGb)}G` : `${SystemStatService.memGb}G` + } + } + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + } NIcon { icon: "memory" - font.pointSize: Style.fontSizeM * scaling - Layout.alignment: Qt.AlignVCenter - } - - NText { - text: showMemoryAsPercent ? `${SystemStatService.memPercent}%` : `${SystemStatService.memGb}G` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + font.pointSize: iconSize + Layout.alignment: Qt.AlignCenter + Layout.row: isVertical ? 1 : 0 + Layout.column: 0 } } } // Network Download Speed Component Item { - Layout.preferredWidth: networkDownloadRow.implicitWidth + Layout.preferredWidth: downloadContent.implicitWidth Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.alignment: Qt.AlignVCenter + Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showNetworkStats - RowLayout { - id: networkDownloadRow + GridLayout { + id: downloadContent anchors.centerIn: parent - spacing: Style.marginXS * scaling + flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: isVertical ? 2 : 1 + columns: isVertical ? 1 : 2 + rowSpacing: Style.marginXXS * scaling + columnSpacing: isVertical ? (Style.marginXXS * scaling) : (Style.marginXS * scaling) + + NText { + text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.rxSpeed) : SystemStatService.formatSpeed(SystemStatService.rxSpeed) + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + } NIcon { icon: "download-speed" - font.pointSize: Style.fontSizeM * scaling - Layout.alignment: Qt.AlignVCenter - } - - NText { - text: SystemStatService.formatSpeed(SystemStatService.rxSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + font.pointSize: iconSize + Layout.alignment: Qt.AlignCenter + Layout.row: isVertical ? 1 : 0 + Layout.column: 0 } } } // Network Upload Speed Component Item { - Layout.preferredWidth: networkUploadRow.implicitWidth + Layout.preferredWidth: uploadContent.implicitWidth Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.alignment: Qt.AlignVCenter + Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showNetworkStats - RowLayout { - id: networkUploadRow + GridLayout { + id: uploadContent anchors.centerIn: parent - spacing: Style.marginXS * scaling + flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: isVertical ? 2 : 1 + columns: isVertical ? 1 : 2 + rowSpacing: Style.marginXXS * scaling + columnSpacing: isVertical ? (Style.marginXXS * scaling) : (Style.marginXS * scaling) + + NText { + text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.txSpeed) : SystemStatService.formatSpeed(SystemStatService.txSpeed) + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + } NIcon { icon: "upload-speed" - font.pointSize: Style.fontSizeM * scaling - Layout.alignment: Qt.AlignVCenter - } - - NText { - text: SystemStatService.formatSpeed(SystemStatService.txSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + font.pointSize: iconSize + Layout.alignment: Qt.AlignCenter + Layout.row: isVertical ? 1 : 0 + Layout.column: 0 } } } // Disk Usage Component (primary drive) Item { - Layout.preferredWidth: diskUsageRow.implicitWidth + Layout.preferredWidth: diskContent.implicitWidth Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.alignment: Qt.AlignVCenter + Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showDiskUsage - RowLayout { - id: diskUsageRow + GridLayout { + id: diskContent anchors.centerIn: parent - spacing: Style.marginXS * scaling - - NIcon { - icon: "storage" - font.pointSize: Style.fontSizeM * scaling - Layout.alignment: Qt.AlignVCenter - } + flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: isVertical ? 2 : 1 + columns: isVertical ? 1 : 2 + rowSpacing: Style.marginXXS * scaling + columnSpacing: isVertical ? (Style.marginXXS * scaling) : (Style.marginXS * scaling) NText { text: `${SystemStatService.diskPercent}%` font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXS * scaling + font.pointSize: textSize font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: Color.mPrimary - } - } - } - } - - // Vertical layout for left/right bars - ColumnLayout { - id: verticalLayout - anchors.centerIn: parent - anchors.topMargin: Style.marginS * scaling - anchors.bottomMargin: Style.marginS * scaling - width: Math.round(28 * scaling) - spacing: Style.marginS * scaling - visible: barPosition === "left" || barPosition === "right" - - // CPU Usage Component - Item { - Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.preferredWidth: Math.round(28 * scaling) - Layout.alignment: Qt.AlignHCenter - visible: showCpuUsage - - Column { - id: cpuUsageRowVertical - anchors.centerIn: parent - spacing: Style.marginXXS * scaling - - NText { - text: `${Math.round(SystemStatService.cpuUsage)}%` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXXS * scaling - font.weight: Style.fontWeightMedium - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - color: Color.mPrimary - } - - NIcon { - icon: "cpu-usage" - font.pointSize: Style.fontSizeS * scaling - anchors.horizontalCenter: parent.horizontalCenter - } - } - } - - // CPU Temperature Component - Item { - Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.preferredWidth: Math.round(28 * scaling) - Layout.alignment: Qt.AlignHCenter - visible: showCpuTemp - - Column { - id: cpuTempRowVertical - anchors.centerIn: parent - spacing: Style.marginXXS * scaling - - NText { - text: `${SystemStatService.cpuTemp}°` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXXS * scaling - font.weight: Style.fontWeightMedium - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - color: Color.mPrimary - } - - NIcon { - icon: "cpu-temperature" - // Fire is so tall, we need to make it smaller - font.pointSize: Style.fontSizeXS * scaling - anchors.horizontalCenter: parent.horizontalCenter - } - } - } - - // Memory Usage Component - Item { - Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.preferredWidth: Math.round(28 * scaling) - Layout.alignment: Qt.AlignHCenter - visible: showMemoryUsage - - Column { - id: memoryUsageRowVertical - anchors.centerIn: parent - spacing: Style.marginXXS * scaling - - NText { - text: showMemoryAsPercent ? `${SystemStatService.memPercent}%` : `${Math.round(SystemStatService.memGb)}G` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXXS * scaling - font.weight: Style.fontWeightMedium - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - color: Color.mPrimary - } - - NIcon { - icon: "memory" - font.pointSize: Style.fontSizeS * scaling - anchors.horizontalCenter: parent.horizontalCenter - } - } - } - - // Network Download Speed Component - Item { - Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.preferredWidth: Math.round(28 * scaling) - Layout.alignment: Qt.AlignHCenter - visible: showNetworkStats - - Column { - id: networkDownloadRowVertical - anchors.centerIn: parent - spacing: Style.marginXXS * scaling - - NText { - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - text: formatCompactSpeed(SystemStatService.rxSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXXS * scaling - font.weight: Style.fontWeightMedium - color: Color.mPrimary - } - - NIcon { - icon: "download-speed" - font.pointSize: Style.fontSizeS * scaling - anchors.horizontalCenter: parent.horizontalCenter - } - } - } - - // Network Upload Speed Component - Item { - Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.preferredWidth: Math.round(28 * scaling) - Layout.alignment: Qt.AlignHCenter - visible: showNetworkStats - - Column { - id: networkUploadRowVertical - anchors.centerIn: parent - spacing: Style.marginXXS * scaling - - NText { - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - text: formatCompactSpeed(SystemStatService.txSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXXS * scaling - font.weight: Style.fontWeightMedium - color: Color.mPrimary - } - - NIcon { - icon: "upload-speed" - font.pointSize: Style.fontSizeS * scaling - anchors.horizontalCenter: parent.horizontalCenter - } - } - } - - // Disk Usage Component (primary drive) - Item { - Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) - Layout.preferredWidth: Math.round(28 * scaling) - Layout.alignment: Qt.AlignHCenter - visible: showDiskUsage - - ColumnLayout { - id: diskUsageRowVertical - anchors.centerIn: parent - spacing: Style.marginXXS * scaling - - NText { - text: `${SystemStatService.diskPercent}%` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeXXS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 } NIcon { icon: "storage" - font.pointSize: Style.fontSizeS * scaling - Layout.alignment: Qt.AlignHCenter + font.pointSize: iconSize + Layout.alignment: Qt.AlignCenter + Layout.row: isVertical ? 1 : 0 + Layout.column: 0 } } } diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml index 3c570ec..d69007c 100644 --- a/Modules/Bar/Widgets/Taskbar.qml +++ b/Modules/Bar/Widgets/Taskbar.qml @@ -1,5 +1,3 @@ -pragma ComponentBehavior - import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -15,18 +13,32 @@ Rectangle { property ShellScreen screen property real scaling: 1.0 - readonly property real itemSize: Style.baseWidgetSize * 0.8 * scaling + readonly property bool isVerticalBar: Settings.data.bar.position === "left" || Settings.data.bar.position === "right" + readonly property bool compact: (Settings.data.bar.density === "compact") + readonly property real itemSize: compact ? Style.capsuleHeight * 0.9 * scaling : Style.capsuleHeight * 0.8 * scaling // Always visible when there are toplevels - implicitWidth: taskbarLayout.implicitWidth + Style.marginM * scaling * 2 - implicitHeight: Math.round(Style.capsuleHeight * scaling) + implicitWidth: isVerticalBar ? Math.round(Style.capsuleHeight * scaling) : taskbarLayout.implicitWidth + Style.marginM * scaling * 2 + implicitHeight: isVerticalBar ? taskbarLayout.implicitHeight + Style.marginM * scaling * 2 : Math.round(Style.capsuleHeight * scaling) radius: Math.round(Style.radiusM * scaling) - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent - RowLayout { + GridLayout { id: taskbarLayout - anchors.centerIn: parent - spacing: Style.marginXXS * root.scaling + anchors.fill: parent + anchors { + leftMargin: isVerticalBar ? undefined : Style.marginM * scaling + rightMargin: isVerticalBar ? undefined : Style.marginM * scaling + topMargin: compact ? 0 : isVerticalBar ? Style.marginM * scaling : undefined + bottomMargin: compact ? 0 : isVerticalBar ? Style.marginM * scaling : undefined + } + + // Configure GridLayout to behave like RowLayout or ColumnLayout + rows: isVerticalBar ? -1 : 1 // -1 means unlimited + columns: isVerticalBar ? 1 : -1 // -1 means unlimited + + rowSpacing: isVerticalBar ? Style.marginXXS * root.scaling : 0 + columnSpacing: isVerticalBar ? 0 : Style.marginXXS * root.scaling Repeater { model: ToplevelManager && ToplevelManager.toplevels ? ToplevelManager.toplevels : [] @@ -43,8 +55,8 @@ Rectangle { Rectangle { id: iconBackground anchors.centerIn: parent - width: root.itemSize * 0.75 - height: root.itemSize * 0.75 + width: parent.width + height: parent.height color: taskbarItem.isActive ? Color.mPrimary : root.color border.width: 0 radius: Math.round(Style.radiusXS * root.scaling) @@ -54,10 +66,11 @@ Rectangle { IconImage { id: appIcon anchors.centerIn: parent - width: Style.marginL * root.scaling - height: Style.marginL * root.scaling + width: parent.width + height: parent.height source: AppIcons.iconForAppId(taskbarItem.modelData.appId) smooth: true + asynchronous: true } } diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index daa4c80..3904a9c 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -16,9 +16,10 @@ Rectangle { property ShellScreen screen property real scaling: 1.0 - readonly property real itemSize: 24 * scaling readonly property string barPosition: Settings.data.bar.position readonly property bool isVertical: barPosition === "left" || barPosition === "right" + readonly property bool compact: (Settings.data.bar.density === "compact") + readonly property real itemSize: isVertical ? width * 0.75 : height * 0.85 function onLoaded() { // When the widget is fully initialized with its props set the screen for the trayMenu @@ -31,7 +32,7 @@ Rectangle { implicitWidth: isVertical ? Math.round(Style.capsuleHeight * scaling) : (trayFlow.implicitWidth + Style.marginS * scaling * 2) implicitHeight: isVertical ? (trayFlow.implicitHeight + Style.marginS * scaling * 2) : Math.round(Style.capsuleHeight * scaling) radius: Math.round(Style.radiusM * scaling) - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent Layout.alignment: Qt.AlignVCenter diff --git a/Modules/Bar/Widgets/Volume.qml b/Modules/Bar/Widgets/Volume.qml index 15e1ddc..bcacd7f 100644 --- a/Modules/Bar/Widgets/Volume.qml +++ b/Modules/Bar/Widgets/Volume.qml @@ -6,6 +6,7 @@ import qs.Commons import qs.Modules.SettingsPanel import qs.Services import qs.Widgets +import qs.Modules.Bar.Extras Item { id: root @@ -71,10 +72,11 @@ Item { } } - NPill { + BarPill { id: pill - rightOpen: BarWidgetRegistry.getNPillDirection(root) + compact: (Settings.data.bar.density === "compact") + rightOpen: BarWidgetRegistry.getPillDirection(root) icon: getIcon() autoHide: false // Important to be false so we can hover as long as we want text: Math.floor(AudioService.volume * 100) diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml index 74b3e73..9bc6058 100644 --- a/Modules/Bar/Widgets/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -13,9 +13,9 @@ NIconButton { property ShellScreen screen property real scaling: 1.0 - sizeRatio: 0.8 - - colorBg: Color.mSurfaceVariant + compact: (Settings.data.bar.density === "compact") + baseSize: Style.capsuleHeight + colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/Workspace.qml b/Modules/Bar/Widgets/Workspace.qml index 8d668a9..1a4f30e 100644 --- a/Modules/Bar/Widgets/Workspace.qml +++ b/Modules/Bar/Widgets/Workspace.qml @@ -32,6 +32,15 @@ Item { } readonly property string barPosition: Settings.data.bar.position + readonly property bool isVertical: barPosition === "left" || barPosition === "right" + readonly property bool compact: (Settings.data.bar.density === "compact") + readonly property real baseDimensionRatio: { + const b = compact ? 0.85 : 0.65 + if (widgetSettings.labelMode === "none") { + return b * 0.75 + } + return b + } readonly property string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode readonly property bool hideUnoccupied: (widgetSettings.hideUnoccupied !== undefined) ? widgetSettings.hideUnoccupied : widgetMetadata.hideUnoccupied @@ -49,47 +58,45 @@ Item { signal workspaceChanged(int workspaceId, color accentColor) - implicitHeight: (barPosition === "left" || barPosition === "right") ? calculatedVerticalHeight() : Math.round(Style.barHeight * scaling) - implicitWidth: (barPosition === "left" || barPosition === "right") ? Math.round(Style.barHeight * scaling) : calculatedHorizontalWidth() + implicitWidth: isVertical ? Math.round(Style.barHeight * scaling) : computeWidth() + implicitHeight: isVertical ? computeHeight() : Math.round(Style.barHeight * scaling) - function calculatedWsWidth(ws) { + function getWorkspaceWidth(ws) { + const d = Style.capsuleHeight * root.baseDimensionRatio if (ws.isFocused) - return Math.round(44 * scaling) - else if (ws.isActive) - return Math.round(28 * scaling) + return d * 2.5 else - return Math.round(20 * scaling) + return d } - function calculatedWsHeight(ws) { + function getWorkspaceHeight(ws) { + const d = Style.capsuleHeight * root.baseDimensionRatio if (ws.isFocused) - return Math.round(44 * scaling) - else if (ws.isActive) - return Math.round(28 * scaling) + return d * 3 else - return Math.round(20 * scaling) + return d } - function calculatedVerticalHeight() { + function computeWidth() { let total = 0 for (var i = 0; i < localWorkspaces.count; i++) { const ws = localWorkspaces.get(i) - total += calculatedWsHeight(ws) + total += getWorkspaceWidth(ws) } total += Math.max(localWorkspaces.count - 1, 0) * spacingBetweenPills total += horizontalPadding * 2 - return total + return Math.round(total) } - function calculatedHorizontalWidth() { + function computeHeight() { let total = 0 for (var i = 0; i < localWorkspaces.count; i++) { const ws = localWorkspaces.get(i) - total += calculatedWsWidth(ws) + total += getWorkspaceHeight(ws) } total += Math.max(localWorkspaces.count - 1, 0) * spacingBetweenPills total += horizontalPadding * 2 - return total + return Math.round(total) } Component.onCompleted: { @@ -173,10 +180,10 @@ Item { Rectangle { id: workspaceBackground - width: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : parent.width - height: (barPosition === "left" || barPosition === "right") ? parent.height : Math.round(Style.capsuleHeight * scaling) + width: isVertical ? Math.round(Style.capsuleHeight * scaling) : parent.width + height: isVertical ? parent.height : Math.round(Style.capsuleHeight * scaling) radius: Math.round(Style.radiusM * scaling) - color: Color.mSurfaceVariant + color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter @@ -187,17 +194,16 @@ Item { id: pillRow spacing: spacingBetweenPills anchors.verticalCenter: workspaceBackground.verticalCenter - width: root.width - horizontalPadding * 2 x: horizontalPadding - visible: barPosition === "top" || barPosition === "bottom" + visible: !isVertical Repeater { id: workspaceRepeaterHorizontal model: localWorkspaces Item { id: workspacePillContainer - height: (labelMode !== "none") ? Math.round(18 * scaling) : Math.round(14 * scaling) - width: root.calculatedWsWidth(model) + width: root.getWorkspaceWidth(model) + height: Style.capsuleHeight * root.baseDimensionRatio Rectangle { id: pill @@ -216,7 +222,7 @@ Item { return model.idx.toString() } } - font.pointSize: model.isFocused ? Style.fontSizeXS * scaling : Style.fontSizeXXS * scaling + font.pointSize: model.isFocused ? workspacePillContainer.height * 0.45 : workspacePillContainer.height * 0.42 font.capitalization: Font.AllUppercase font.family: Settings.data.ui.fontFixed font.weight: Style.fontWeightBold @@ -332,17 +338,16 @@ Item { id: pillColumn spacing: spacingBetweenPills anchors.horizontalCenter: workspaceBackground.horizontalCenter - height: root.height - horizontalPadding * 2 y: horizontalPadding - visible: barPosition === "left" || barPosition === "right" + visible: isVertical Repeater { id: workspaceRepeaterVertical model: localWorkspaces Item { id: workspacePillContainerVertical - width: (labelMode !== "none") ? Math.round(18 * scaling) : Math.round(14 * scaling) - height: root.calculatedWsHeight(model) + width: Style.capsuleHeight * root.baseDimensionRatio + height: root.getWorkspaceHeight(model) Rectangle { id: pillVertical @@ -361,7 +366,7 @@ Item { return model.idx.toString() } } - font.pointSize: model.isFocused ? Style.fontSizeXS * scaling : Style.fontSizeXXS * scaling + font.pointSize: model.isFocused ? workspacePillContainerVertical.width * 0.45 : workspacePillContainerVertical.width * 0.42 font.capitalization: Font.AllUppercase font.family: Settings.data.ui.fontFixed font.weight: Style.fontWeightBold diff --git a/Modules/BluetoothPanel/BluetoothPanel.qml b/Modules/BluetoothPanel/BluetoothPanel.qml index 12a5ce0..aa743f0 100644 --- a/Modules/BluetoothPanel/BluetoothPanel.qml +++ b/Modules/BluetoothPanel/BluetoothPanel.qml @@ -53,7 +53,7 @@ NPanel { enabled: Settings.data.network.bluetoothEnabled icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "refresh" tooltipText: "Refresh Devices" - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 onClicked: { if (BluetoothService.adapter) { BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering @@ -64,7 +64,7 @@ NPanel { NIconButton { icon: "close" tooltipText: "Close." - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 onClicked: { root.close() } diff --git a/Modules/Calendar/Calendar.qml b/Modules/Calendar/Calendar.qml index 80c6fd3..80b994a 100644 --- a/Modules/Calendar/Calendar.qml +++ b/Modules/Calendar/Calendar.qml @@ -12,7 +12,6 @@ NPanel { preferredWidth: 340 preferredHeight: 320 - panelAnchorRight: Settings.data.bar.position === "right" // Main Column panelContent: ColumnLayout { diff --git a/Modules/Launcher/Plugins/ApplicationsPlugin.qml b/Modules/Launcher/Plugins/ApplicationsPlugin.qml index e6eced4..2d21a5f 100644 --- a/Modules/Launcher/Plugins/ApplicationsPlugin.qml +++ b/Modules/Launcher/Plugins/ApplicationsPlugin.qml @@ -79,8 +79,11 @@ Item { "icon": app.icon || "application-x-executable", "isImage": false, "onActivate": function () { - Logger.log("ApplicationsPlugin", `Launching: ${app.name}`) + // Close the launcher/NPanel immediately without any animations. + // Ensures we are not preventing the future focusing of the app + launcher.closeCompleted() + Logger.log("ApplicationsPlugin", `Launching: ${app.name}`) if (Settings.data.appLauncher.useApp2Unit && app.id) { Logger.log("ApplicationsPlugin", `Using app2unit for: ${app.id}`) if (app.runInTerminal) @@ -89,11 +92,9 @@ Item { Quickshell.execDetached(["app2unit", "--"].concat(app.command)) } else if (app.execute) { app.execute() - } else if (app.exec) { - // Fallback to manual execution - Process.execute(app.exec) + } else { + Logger.log("ApplicationsPlugin", `Could not launch: ${app.name}`) } - launcher.close() } } } diff --git a/Modules/Notification/Notification.qml b/Modules/Notification/Notification.qml index 940c6d6..68d0906 100644 --- a/Modules/Notification/Notification.qml +++ b/Modules/Notification/Notification.qml @@ -326,7 +326,7 @@ Variants { NIconButton { icon: "close" tooltipText: "Close." - sizeRatio: 0.6 + baseSize: Style.baseWidgetSize * 0.6 anchors.top: parent.top anchors.topMargin: Style.marginM * scaling anchors.right: parent.right diff --git a/Modules/Notification/NotificationHistoryPanel.qml b/Modules/Notification/NotificationHistoryPanel.qml index 0a9e852..fd5be5f 100644 --- a/Modules/Notification/NotificationHistoryPanel.qml +++ b/Modules/Notification/NotificationHistoryPanel.qml @@ -14,7 +14,6 @@ NPanel { preferredWidth: 380 preferredHeight: 500 - panelAnchorRight: Settings.data.bar.position === "right" panelKeyboardFocus: true panelContent: Rectangle { @@ -48,15 +47,15 @@ NPanel { NIconButton { icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell" tooltipText: Settings.data.notifications.doNotDisturb ? "'Do Not Disturb' is enabled." : "'Do Not Disturb' is disabled." - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb onRightClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb } NIconButton { icon: "trash" - tooltipText: "Clear history" - sizeRatio: 0.8 + tooltipText: "Clear history." + baseSize: Style.baseWidgetSize * 0.8 onClicked: { NotificationService.clearHistory() root.close() @@ -66,7 +65,7 @@ NPanel { NIconButton { icon: "close" tooltipText: "Close." - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 onClicked: { root.close() } @@ -136,7 +135,7 @@ NPanel { width: notificationList.width height: notificationLayout.implicitHeight + (Style.marginM * scaling * 2) radius: Style.radiusM * scaling - color: notificationMouseArea.containsMouse ? Color.mTertiary : Color.mSurfaceVariant + color: Color.mSurfaceVariant border.color: Qt.alpha(Color.mOutline, Style.opacityMedium) border.width: Math.max(1, Style.borderS * scaling) @@ -169,7 +168,7 @@ NPanel { text: (summary || "No summary").substring(0, 100) font.pointSize: Style.fontSizeM * scaling font.weight: Font.Medium - color: notificationMouseArea.containsMouse ? Color.mOnTertiary : Color.mPrimary + color: Color.mPrimary wrapMode: Text.Wrap Layout.fillWidth: true maximumLineCount: 2 @@ -179,7 +178,7 @@ NPanel { NText { text: (body || "").substring(0, 150) font.pointSize: Style.fontSizeXS * scaling - color: notificationMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface + color: Color.mOnSurface wrapMode: Text.Wrap Layout.fillWidth: true maximumLineCount: 3 @@ -190,7 +189,7 @@ NPanel { NText { text: NotificationService.formatTimestamp(timestamp) font.pointSize: Style.fontSizeXS * scaling - color: notificationMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface + color: Color.mOnSurface Layout.fillWidth: true } } @@ -198,8 +197,8 @@ NPanel { // Delete button NIconButton { icon: "trash" - tooltipText: "Delete notification" - sizeRatio: 0.7 + tooltipText: "Delete notification." + baseSize: Style.baseWidgetSize * 0.7 Layout.alignment: Qt.AlignTop onClicked: { diff --git a/Modules/SettingsPanel/Bar/BarSectionEditor.qml b/Modules/SettingsPanel/Bar/BarSectionEditor.qml index 0c1919b..79ee7c1 100644 --- a/Modules/SettingsPanel/Bar/BarSectionEditor.qml +++ b/Modules/SettingsPanel/Bar/BarSectionEditor.qml @@ -14,6 +14,8 @@ NBox { property var widgetModel: [] property var availableWidgets: [] + readonly property real miniButtonSize: Style.baseWidgetSize * 0.65 + signal addWidget(string widgetId, string section) signal removeWidget(string section, int index) signal reorderWidget(string section, int fromIndex, int toIndex) @@ -178,7 +180,7 @@ NBox { active: BarWidgetRegistry.widgetHasUserSettings(modelData.id) sourceComponent: NIconButton { icon: "settings" - sizeRatio: 0.6 + baseSize: miniButtonSize colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight) colorBg: Color.mOnSurface colorFg: Color.mOnPrimary @@ -218,7 +220,7 @@ NBox { NIconButton { icon: "close" - sizeRatio: 0.6 + baseSize: miniButtonSize colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight) colorBg: Color.mOnSurface colorFg: Color.mOnPrimary diff --git a/Modules/SettingsPanel/Tabs/AudioTab.qml b/Modules/SettingsPanel/Tabs/AudioTab.qml index b0d42ac..463185c 100644 --- a/Modules/SettingsPanel/Tabs/AudioTab.qml +++ b/Modules/SettingsPanel/Tabs/AudioTab.qml @@ -298,7 +298,7 @@ ColumnLayout { NIconButton { icon: "close" - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 Layout.alignment: Qt.AlignVCenter Layout.rightMargin: Style.marginXS * scaling onClicked: { diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index 1b4353c..8a005bc 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -45,32 +45,52 @@ ColumnLayout { description: "Configure bar appearance and positioning." } - RowLayout { - NComboBox { - Layout.fillWidth: true - label: "Bar Position" - description: "Choose where to place the bar on the screen." - model: ListModel { - ListElement { - key: "top" - name: "Top" - } - ListElement { - key: "bottom" - name: "Bottom" - } - ListElement { - key: "left" - name: "Left" - } - ListElement { - key: "right" - name: "Right" - } + NComboBox { + Layout.fillWidth: true + label: "Bar Position" + description: "Choose where to place the bar on the screen." + model: ListModel { + ListElement { + key: "top" + name: "Top" + } + ListElement { + key: "bottom" + name: "Bottom" + } + ListElement { + key: "left" + name: "Left" + } + ListElement { + key: "right" + name: "Right" } - currentKey: Settings.data.bar.position - onSelected: key => Settings.data.bar.position = key } + currentKey: Settings.data.bar.position + onSelected: key => Settings.data.bar.position = key + } + + NComboBox { + Layout.fillWidth: true + label: "Bar Density" + description: "Choose the density of the bar." + model: ListModel { + ListElement { + key: "compact" + name: "Compact" + } + ListElement { + key: "default" + name: "Default" + } + ListElement { + key: "comfortable" + name: "Comfortable" + } + } + currentKey: Settings.data.bar.density + onSelected: key => Settings.data.bar.density = key } ColumnLayout { @@ -92,6 +112,15 @@ ColumnLayout { text: Math.floor(Settings.data.bar.backgroundOpacity * 100) + "%" } } + + NToggle { + Layout.fillWidth: true + label: "Show Capsule" + description: "Adds a capsule behind each widget to improve readability on transparent bars." + checked: Settings.data.bar.showCapsule + onToggled: checked => Settings.data.bar.showCapsule = checked + } + NToggle { Layout.fillWidth: true label: "Floating Bar" @@ -163,40 +192,6 @@ ColumnLayout { Layout.bottomMargin: Style.marginXL * scaling } - // Monitor Configuration - ColumnLayout { - spacing: Style.marginM * scaling - Layout.fillWidth: true - - NHeader { - label: "Monitors Configuration" - description: "Choose which monitors should display the bar." - } - - Repeater { - model: Quickshell.screens || [] - delegate: NCheckbox { - Layout.fillWidth: true - label: `${modelData.name || "Unknown"}${modelData.model ? `: ${modelData.model}` : ""}` - description: `${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})` - checked: (Settings.data.bar.monitors || []).indexOf(modelData.name) !== -1 - onToggled: checked => { - if (checked) { - Settings.data.bar.monitors = addMonitor(Settings.data.bar.monitors, modelData.name) - } else { - Settings.data.bar.monitors = removeMonitor(Settings.data.bar.monitors, modelData.name) - } - } - } - } - } - - NDivider { - Layout.fillWidth: true - Layout.topMargin: Style.marginXL * scaling - Layout.bottomMargin: Style.marginXL * scaling - } - // Widgets Management Section ColumnLayout { spacing: Style.marginXXS * scaling @@ -264,6 +259,40 @@ ColumnLayout { Layout.bottomMargin: Style.marginXL * scaling } + // Monitor Configuration + ColumnLayout { + spacing: Style.marginM * scaling + Layout.fillWidth: true + + NHeader { + label: "Monitors Configuration" + description: "Show bar on specific monitors. Defaults to all if none are chosen." + } + + Repeater { + model: Quickshell.screens || [] + delegate: NCheckbox { + Layout.fillWidth: true + label: modelData.name || "Unknown" + description: `${modelData.model} - ${modelData.width}x${modelData.height} [x:${modelData.x} y:${modelData.y}]` + checked: (Settings.data.bar.monitors || []).indexOf(modelData.name) !== -1 + onToggled: checked => { + if (checked) { + Settings.data.bar.monitors = addMonitor(Settings.data.bar.monitors, modelData.name) + } else { + Settings.data.bar.monitors = removeMonitor(Settings.data.bar.monitors, modelData.name) + } + } + } + } + } + + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginXL * scaling + Layout.bottomMargin: Style.marginXL * scaling + } + // --------------------------------- // Signal functions // --------------------------------- diff --git a/Modules/SettingsPanel/Tabs/DisplayTab.qml b/Modules/SettingsPanel/Tabs/DisplayTab.qml index b5e41f8..65c14ef 100644 --- a/Modules/SettingsPanel/Tabs/DisplayTab.qml +++ b/Modules/SettingsPanel/Tabs/DisplayTab.qml @@ -87,19 +87,9 @@ ColumnLayout { anchors.margins: Style.marginL * scaling spacing: Style.marginXXS * scaling - NText { - text: (`${modelData.name}: ${modelData.model}` || "Unknown") - font.pointSize: Style.fontSizeL * scaling - font.weight: Style.fontWeightBold - color: Color.mPrimary - } - - NText { - text: `Resolution: ${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})` - font.pointSize: Style.fontSizeXS * scaling - color: Color.mOnSurfaceVariant - wrapMode: Text.WordWrap - Layout.fillWidth: true + NLabel { + label: modelData.name || "Unknown" + description: `${modelData.model} - ${modelData.width}x${modelData.height} [x:${modelData.x} y:${modelData.y}]` } // Scale @@ -134,8 +124,8 @@ ColumnLayout { NIconButton { icon: "refresh" - sizeRatio: 0.8 - tooltipText: "Reset scaling" + baseSize: Style.baseWidgetSize * 0.9 + tooltipText: "Reset scaling." onClicked: ScalingService.setScreenScale(modelData, 1.0) anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter diff --git a/Modules/SettingsPanel/Tabs/DockTab.qml b/Modules/SettingsPanel/Tabs/DockTab.qml index 293378c..b8a22ec 100644 --- a/Modules/SettingsPanel/Tabs/DockTab.qml +++ b/Modules/SettingsPanel/Tabs/DockTab.qml @@ -93,15 +93,15 @@ ColumnLayout { NHeader { label: "Monitors Configuration" - description: "Choose which monitors should display the dock." + description: "Show dock on specific monitors." } Repeater { model: Quickshell.screens || [] delegate: NCheckbox { Layout.fillWidth: true - label: `${modelData.name || "Unknown"}${modelData.model ? `: ${modelData.model}` : ""}` - description: `${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})` + label: modelData.name || "Unknown" + description: `${modelData.model} - ${modelData.width}x${modelData.height} [x:${modelData.x} y:${modelData.y}]` checked: (Settings.data.dock.monitors || []).indexOf(modelData.name) !== -1 onToggled: checked => { if (checked) { diff --git a/Modules/SettingsPanel/Tabs/NotificationTab.qml b/Modules/SettingsPanel/Tabs/NotificationTab.qml index fa6d268..7d7a7f2 100644 --- a/Modules/SettingsPanel/Tabs/NotificationTab.qml +++ b/Modules/SettingsPanel/Tabs/NotificationTab.qml @@ -46,40 +46,6 @@ ColumnLayout { Layout.bottomMargin: Style.marginXL * scaling } - // Monitor Configuration - ColumnLayout { - spacing: Style.marginM * scaling - Layout.fillWidth: true - - NHeader { - label: "Monitors Configuration" - description: "Choose which monitors should display notifications." - } - - Repeater { - model: Quickshell.screens || [] - delegate: NCheckbox { - Layout.fillWidth: true - label: `${modelData.name || "Unknown"}${modelData.model ? `: ${modelData.model}` : ""}` - description: `${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})` - checked: (Settings.data.notifications.monitors || []).indexOf(modelData.name) !== -1 - onToggled: checked => { - if (checked) { - Settings.data.notifications.monitors = addMonitor(Settings.data.notifications.monitors, modelData.name) - } else { - Settings.data.notifications.monitors = removeMonitor(Settings.data.notifications.monitors, modelData.name) - } - } - } - } - } - - NDivider { - Layout.fillWidth: true - Layout.topMargin: Style.marginXL * scaling - Layout.bottomMargin: Style.marginXL * scaling - } - // Notification Duration Settings ColumnLayout { spacing: Style.marginL * scaling @@ -159,4 +125,38 @@ ColumnLayout { Layout.topMargin: Style.marginXL * scaling Layout.bottomMargin: Style.marginXL * scaling } + + // Monitor Configuration + ColumnLayout { + spacing: Style.marginM * scaling + Layout.fillWidth: true + + NHeader { + label: "Monitors Configuration" + description: "Show bar on specific monitors. Defaults to all if none are chosen." + } + + Repeater { + model: Quickshell.screens || [] + delegate: NCheckbox { + Layout.fillWidth: true + label: modelData.name || "Unknown" + description: `${modelData.model} - ${modelData.width}x${modelData.height} [x:${modelData.x} y:${modelData.y}]` + checked: (Settings.data.notifications.monitors || []).indexOf(modelData.name) !== -1 + onToggled: checked => { + if (checked) { + Settings.data.notifications.monitors = addMonitor(Settings.data.notifications.monitors, modelData.name) + } else { + Settings.data.notifications.monitors = removeMonitor(Settings.data.notifications.monitors, modelData.name) + } + } + } + } + } + + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginXL * scaling + Layout.bottomMargin: Style.marginXL * scaling + } } diff --git a/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml b/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml index 05cb66a..63f9f70 100644 --- a/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml +++ b/Modules/SettingsPanel/Tabs/ScreenRecorderTab.qml @@ -64,7 +64,7 @@ ColumnLayout { // Source NComboBox { label: "Video Source" - description: "Portal is recommend, if you get artifacts try Screen." + description: "Portal is recommended, if you get artifacts try Screen." model: ListModel { ListElement { key: "portal" diff --git a/Modules/Toast/SimpleToast.qml b/Modules/Toast/SimpleToast.qml index bc51858..90a8416 100644 --- a/Modules/Toast/SimpleToast.qml +++ b/Modules/Toast/SimpleToast.qml @@ -136,7 +136,7 @@ Rectangle { colorBorder: Color.transparent colorBorderHover: Color.mOutline - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 Layout.alignment: Qt.AlignTop onClicked: root.hide() diff --git a/Modules/WiFiPanel/WiFiPanel.qml b/Modules/WiFiPanel/WiFiPanel.qml index bfda627..190110c 100644 --- a/Modules/WiFiPanel/WiFiPanel.qml +++ b/Modules/WiFiPanel/WiFiPanel.qml @@ -57,7 +57,7 @@ NPanel { NIconButton { icon: "refresh" tooltipText: "Refresh" - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning onClicked: NetworkService.scan() } @@ -65,7 +65,7 @@ NPanel { NIconButton { icon: "close" tooltipText: "Close." - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 onClicked: root.close() } } @@ -106,7 +106,7 @@ NPanel { NIconButton { icon: "close" - sizeRatio: 0.6 + baseSize: Style.baseWidgetSize * 0.6 onClicked: NetworkService.lastError = "" } } @@ -368,7 +368,7 @@ NPanel { visible: (modelData.existing || modelData.cached) && !modelData.connected && NetworkService.connectingTo !== modelData.ssid && NetworkService.forgettingNetwork !== modelData.ssid && NetworkService.disconnectingFrom !== modelData.ssid icon: "trash" tooltipText: "Forget network" - sizeRatio: 0.7 + baseSize: Style.baseWidgetSize * 0.8 onClicked: expandedSsid = expandedSsid === modelData.ssid ? "" : modelData.ssid } @@ -478,7 +478,7 @@ NPanel { NIconButton { icon: "close" - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 onClicked: { passwordSsid = "" passwordInput = "" @@ -532,7 +532,7 @@ NPanel { NIconButton { icon: "close" - sizeRatio: 0.8 + baseSize: Style.baseWidgetSize * 0.8 onClicked: expandedSsid = "" } } diff --git a/README.md b/README.md index 0429709..d839edc 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,23 @@ Start the Shell with: `qs -c noctalia-shell` Access settings through the side panel (top right button) to configure weather, wallpapers, screen recording, audio, network, and theme options. Configuration is usually stored in ~/.config/noctalia. +### Some of my app icons are missing! + +The issue is most likely that you did not set up your environment variables properly. +Example environment variables that you can use one of the following: + +If you already have an icon theme set for GTK then you can use this one: +- `QT_QPA_PLATFORMTHEME=gtk3` + +You can also use Qt6ct to set your icon theme, for that you can use: +- `QT_QPA_PLATFORMTHEME=qt6ct` + +If you don't have either of those set then you can just use: +- `QS_ICON_THEME="youricontheme"` + +**Any of these environment variables should go into `/etc/environment` (you need to reboot afterwards). For NixOS you can use `environment.variables` or `home.sessionVariables`.** + + ### Application Launcher The launcher supports special commands for enhanced functionality: @@ -278,10 +295,6 @@ window-rule { clip-to-geometry true } -layer-rule { - match namespace="^quickshell-wallpaper$" -} - layer-rule { match namespace="^quickshell-overview$" place-within-backdrop true diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index 392b570..f12a94c 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -207,7 +207,7 @@ Singleton { return (widgetMetadata[id] !== undefined) && (widgetMetadata[id].allowUserSettings === true) } - function getNPillDirection(widget) { + function getPillDirection(widget) { try { if (widget.section === "left") { return true diff --git a/Services/PanelService.qml b/Services/PanelService.qml index 8922034..f1df973 100644 --- a/Services/PanelService.qml +++ b/Services/PanelService.qml @@ -16,6 +16,9 @@ Singleton { property var registeredPanels: ({}) + signal willOpen + signal willClose + // Register this panel function registerPanel(panel) { registeredPanels[panel.objectName] = panel @@ -38,6 +41,14 @@ Singleton { openedPanel.close() } openedPanel = panel + + // emit signal + willOpen() + } + + function willClosePanel(panel) { + // emit signal + willClose() } function closedPanel(panel) { diff --git a/Services/SystemStatService.qml b/Services/SystemStatService.qml index a8d37d1..7a87e5c 100644 --- a/Services/SystemStatService.qml +++ b/Services/SystemStatService.qml @@ -333,6 +333,26 @@ Singleton { } } + // Compact speed formatter for vertical bar display + function formatCompactSpeed(bytesPerSecond) { + if (!bytesPerSecond || bytesPerSecond <= 0) + return "0" + const units = ["", "K", "M", "G"] + let value = bytesPerSecond + let unitIndex = 0 + while (value >= 1024 && unitIndex < units.length - 1) { + value = value / 1024.0 + unitIndex++ + } + // Promote at ~100 of current unit (e.g., 100k -> ~0.1M shown as 0.1M or 0M if rounded) + if (unitIndex < units.length - 1 && value >= 100) { + value = value / 1024.0 + unitIndex++ + } + const display = Math.round(value).toString() + return display + units[unitIndex] + } + // ------------------------------------------------------- // Function to start fetching and computing the cpu temperature function updateCpuTemperature() { diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index c56a489..92bd577 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -8,7 +8,7 @@ Singleton { id: root // Public properties - property string baseVersion: "2.9.2" + property string baseVersion: "2.11.0" property bool isDevelopment: false property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` diff --git a/Widgets/NColorPicker.qml b/Widgets/NColorPicker.qml index 92525dd..8eee16b 100644 --- a/Widgets/NColorPicker.qml +++ b/Widgets/NColorPicker.qml @@ -13,7 +13,7 @@ Rectangle { signal colorSelected(color color) implicitWidth: 150 * scaling - implicitHeight: 40 * scaling + implicitHeight: Math.round(Style.baseWidgetSize * 1.1 * scaling) radius: Style.radiusM * scaling color: Color.mSurface @@ -40,12 +40,16 @@ Rectangle { RowLayout { anchors.fill: parent - anchors.margins: Style.marginS * scaling + anchors { + leftMargin: Style.marginL * scaling + rightMargin: Style.marginL * scaling + } spacing: Style.marginS * scaling + // Color preview circle Rectangle { - Layout.preferredWidth: 24 * scaling - Layout.preferredHeight: 24 * scaling + Layout.preferredWidth: root.height * 0.6 * scaling + Layout.preferredHeight: root.height * 0.6 * scaling radius: Layout.preferredWidth * 0.5 color: root.selectedColor border.color: Color.mOutline @@ -56,11 +60,14 @@ Rectangle { text: root.selectedColor.toString().toUpperCase() font.family: Settings.data.ui.fontFixed Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } NIcon { icon: "color-picker" color: Color.mOnSurfaceVariant + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } } } diff --git a/Widgets/NIconButton.qml b/Widgets/NIconButton.qml index 07f2798..5989190 100644 --- a/Widgets/NIconButton.qml +++ b/Widgets/NIconButton.qml @@ -7,14 +7,14 @@ import qs.Services Rectangle { id: root - // Multiplier to control how large the button container is relative to Style.baseWidgetSize - property real sizeRatio: 1.0 + property real baseSize: Style.baseWidgetSize property string icon property string tooltipText property bool enabled: true property bool allowClickWhenDisabled: false property bool hovering: false + property bool compact: false property color colorBg: Color.mSurfaceVariant property color colorFg: Color.mPrimary @@ -29,8 +29,8 @@ Rectangle { signal rightClicked signal middleClicked - implicitWidth: Math.round(Style.baseWidgetSize * scaling * sizeRatio) - implicitHeight: Math.round(Style.baseWidgetSize * scaling * sizeRatio) + implicitWidth: Math.round(baseSize * scaling) + implicitHeight: Math.round(baseSize * scaling) opacity: root.enabled ? Style.opacityFull : Style.opacityMedium color: root.enabled && root.hovering ? colorBgHover : colorBg @@ -47,7 +47,7 @@ Rectangle { NIcon { icon: root.icon - font.pointSize: Math.max(1, root.width * 0.47) + font.pointSize: Math.max(1, root.compact ? root.width * 0.65 : root.width * 0.48) color: root.enabled && root.hovering ? colorFgHover : colorFg // Center horizontally x: (root.width - width) / 2 diff --git a/Widgets/NPanel.qml b/Widgets/NPanel.qml index ef286f1..5b4b898 100644 --- a/Widgets/NPanel.qml +++ b/Widgets/NPanel.qml @@ -38,9 +38,9 @@ Loader { readonly property real originalOpacity: 0.0 property real scaleValue: originalScale property real opacityValue: originalOpacity + property real dimmingOpacity: 0 property alias isClosing: hideTimer.running - readonly property string barPosition: Settings.data.bar.position signal opened signal closed @@ -109,9 +109,11 @@ Loader { // ----------------------------------------- function close() { + dimmingOpacity = 0 scaleValue = originalScale opacityValue = originalOpacity hideTimer.start() + PanelService.willClosePanel(root) } // ----------------------------------------- @@ -141,10 +143,16 @@ Loader { // PanelWindow has its own screen property inherited of QsWindow property real scaling: ScalingService.getScreenScale(screen) - readonly property real barHeight: Math.round(Style.barHeight * scaling) - readonly property real barWidth: Math.round(Style.barHeight * scaling) - readonly property bool barAtBottom: Settings.data.bar.position === "bottom" + + readonly property string barPosition: Settings.data.bar.position + readonly property bool isVertical: barPosition === "left" || barPosition === "right" readonly property bool barIsVisible: (screen !== null) && (Settings.data.bar.monitors.includes(screen.name) || (Settings.data.bar.monitors.length === 0)) + readonly property real verticalBarWidth: Math.round(Style.barHeight * scaling) + + Component.onCompleted: { + Logger.log("NPanel", "Opened", root.objectName) + dimmingOpacity = Style.opacityHeavy + } Connections { target: ScalingService @@ -169,16 +177,14 @@ Loader { visible: true - // Dim desktop if required - color: (root.active && !root.isClosing && Settings.data.general.dimDesktop) ? Qt.alpha(Color.mShadow, Style.opacityHeavy) : Color.transparent - + color: Settings.data.general.dimDesktop ? Qt.alpha(Color.mShadow, dimmingOpacity) : Color.transparent WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.namespace: "noctalia-panel" WlrLayershell.keyboardFocus: root.panelKeyboardFocus ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None Behavior on color { ColorAnimation { - duration: Style.animationNormal + duration: Style.animationSlow } } @@ -186,29 +192,6 @@ Loader { anchors.left: true anchors.right: true anchors.bottom: true - margins.top: { - if (!barIsVisible || barAtBottom) { - return 0 - } - switch (Settings.data.bar.position) { - case "top": - return (Style.barHeight + Style.marginM) * scaling + (Settings.data.bar.floating && !panelAnchorVerticalCenter ? Settings.data.bar.marginVertical * Style.marginXL * scaling : 0) - default: - return Style.marginM * scaling - } - } - - margins.bottom: { - if (!barIsVisible || !barAtBottom) { - return 0 - } - switch (Settings.data.bar.position) { - case "bottom": - return (Style.barHeight + Style.marginM) * scaling + (Settings.data.bar.floating && !panelAnchorVerticalCenter ? Settings.data.bar.marginVertical * Style.marginXL * scaling : 0) - default: - return 0 - } - } // Close any panel with Esc without requiring focus Shortcut { @@ -225,6 +208,7 @@ Loader { onClicked: root.close() } + // The actual panel's content Rectangle { id: panelBackground color: panelBackgroundColor @@ -255,130 +239,131 @@ Loader { scale: root.scaleValue opacity: root.opacityValue - x: calculatedX y: calculatedY - property int calculatedX: { - var barPosition = Settings.data.bar.position - - // Check anchor properties first, even when using button positioning - if (!panelAnchorHorizontalCenter && panelAnchorLeft) { - return Math.round(Style.marginS * scaling) - } else if (!panelAnchorHorizontalCenter && panelAnchorRight) { - // For right anchor, consider bar position - if (barPosition === "right") { - // If bar is on right, position panel to the left of the bar - var maxX = panelWindow.width - barWidth - panelBackground.width - (Style.marginS * scaling) - - // If we have button position, position close to the button like working panels - if (root.useButtonPosition) { - // Use the same logic as working panels - position at edge of bar with spacing - var maxXWithSpacing = panelWindow.width - barWidth - panelBackground.width - // Add spacing - more if screen corners are disabled, less if enabled - if (!Settings.data.general.showScreenCorners || Settings.data.bar.floating) { - maxXWithSpacing -= Style.marginL * scaling - } else { - maxXWithSpacing -= Style.marginM * scaling - } - return Math.round(maxXWithSpacing) - } else { - return Math.round(maxX) - } - } else { - // Default right positioning - var rightX = panelWindow.width - panelBackground.width - (Style.marginS * scaling) - return Math.round(rightX) - } - } else if (root.useButtonPosition) { - // Position panel relative to button (only if no explicit anchoring) - var targetX - - // For vertical bars, position panel close to the button - if (barPosition === "left") { - // Position panel to the right of the left bar, close to the button - var minX = barWidth - // Add spacing - more if screen corners are disabled, less if enabled - if (!Settings.data.general.showScreenCorners || Settings.data.bar.floating) { - minX += Style.marginL * scaling - } else { - minX += Style.marginM * scaling - } - targetX = minX - } else if (barPosition === "right") { - // Position panel to the left of the right bar, close to the button - var maxX = panelWindow.width - barWidth - panelBackground.width - // Add spacing - more if screen corners are disabled, less if enabled - if (!Settings.data.general.showScreenCorners || Settings.data.bar.floating) { - maxX -= Style.marginL * scaling - } else { - maxX -= Style.marginM * scaling - } - targetX = maxX - } else { - // For horizontal bars, center panel on button - targetX = root.buttonPosition.x + (root.buttonWidth / 2) - (panelBackground.width / 2) - } - - // Keep panel within screen bounds - var maxScreenX = panelWindow.width - panelBackground.width - (Style.marginS * scaling) - var minScreenX = Style.marginS * scaling - - return Math.round(Math.max(minScreenX, Math.min(targetX, maxScreenX))) - } else { - // For vertical bars, center but avoid bar overlap - var centerX = (panelWindow.width - panelBackground.width) / 2 - if (barPosition === "left") { - var minX = barWidth - // Add spacing - more if screen corners are disabled, less if enabled - if (!Settings.data.general.showScreenCorners || Settings.data.bar.floating) { - minX += Style.marginL * scaling - } else { - minX += Style.marginM * scaling - } - centerX = Math.max(centerX, minX) - } else if (barPosition === "right") { - // For right bar, center but ensure it doesn't overlap with the bar - var maxX = panelWindow.width - barWidth - panelBackground.width - // Add spacing - more if screen corners are disabled, less if enabled - if (!Settings.data.general.showScreenCorners || Settings.data.bar.floating) { - maxX -= Style.marginL * scaling - } else { - maxX -= Style.marginM * scaling - } - centerX = Math.min(centerX, maxX) - } - return Math.round(centerX) + // --------------------------------------------- + // Does not account for corners are they are negligible and helps keep the code clean. + // --------------------------------------------- + property real marginTop: { + if (!barIsVisible) { + return 0 + } + switch (barPosition || panelAnchorVerticalCenter) { + case "top": + return (Style.barHeight + Style.marginS) * scaling + (Settings.data.bar.floating ? Settings.data.bar.marginVertical * 2 * Style.marginXL * scaling : 0) + default: + return Style.marginS * scaling } } - property int calculatedY: { - var barPosition = Settings.data.bar.position + property real marginBottom: { + if (!barIsVisible || panelAnchorVerticalCenter) { + return 0 + } + switch (barPosition) { + case "bottom": + return (Style.barHeight + Style.marginS) * scaling + (Settings.data.bar.floating ? Settings.data.bar.marginVertical * 2 * Style.marginXL * scaling : 0) + default: + return Style.marginS * scaling + } + } - if (root.useButtonPosition) { - // Position panel relative to button - var targetY = root.buttonPosition.y + (root.buttonHeight / 2) - (panelBackground.height / 2) + property real marginLeft: { + if (!barIsVisible || panelAnchorHorizontalCenter) { + return 0 + } + switch (barPosition) { + case "left": + return (Style.barHeight + Style.marginS) * scaling + (Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * 2 * Style.marginXL * scaling : 0) + default: + return Style.marginS * scaling + } + } - // Keep panel within screen bounds - var maxY = panelWindow.height - panelBackground.height - (Style.marginS * scaling) - var minY = Style.marginS * scaling + property real marginRight: { + if (!barIsVisible || panelAnchorHorizontalCenter) { + return 0 + } + switch (barPosition) { + case "right": + return (Style.barHeight + Style.marginS) * scaling + (Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * 2 * Style.marginXL * scaling : 0) + default: + return Style.marginS * scaling + } + } - return Math.round(Math.max(minY, Math.min(targetY, maxY))) - } else if (panelAnchorVerticalCenter) { - return Math.round((panelWindow.height - panelBackground.height) / 2) - } else if (panelAnchorBottom) { - return Math.round(panelWindow.height - panelBackground.height - (Style.marginS * scaling)) - } else if (panelAnchorTop) { - return Math.round(Style.marginS * scaling) - } else if (barPosition === "left" || barPosition === "right") { - // For vertical bars, center vertically - return Math.round((panelWindow.height - panelBackground.height) / 2) - } else if (!barAtBottom) { - // Below the top bar - return Math.round(Style.marginS * scaling) + // --------------------------------------------- + property int calculatedX: { + // Priority to fixed anchoring + if (panelAnchorHorizontalCenter) { + return Math.round((panelWindow.width - panelBackground.width) / 2) + } else if (panelAnchorLeft) { + return marginLeft + } else if (panelAnchorRight) { + return Math.round(panelWindow.width - panelBackground.width - marginRight) + } + + // No fixed anchoring + if (isVertical) { + // Vertical bar + if (barPosition === "right") { + // To the left of the right bar + return Math.round(panelWindow.width - panelBackground.width - marginRight) + } else { + // To the right of the left bar + return marginLeft + } } else { - // Above the bottom bar - return Math.round(panelWindow.height - panelBackground.height - (Style.marginS * scaling)) + // Horizontal bar + if (root.useButtonPosition) { + // Position panel relative to button + var targetX = buttonPosition.x + (buttonWidth / 2) - (panelBackground.width / 2) + // Keep panel within screen bounds + var maxX = panelWindow.width - panelBackground.width - marginRight + var minX = marginLeft + return Math.round(Math.max(minX, Math.min(targetX, maxX))) + } else { + // Fallback to center horizontally + return Math.round((panelWindow.width - panelBackground.width) / 2) + } + } + } + + // --------------------------------------------- + property int calculatedY: { + // Priority to fixed anchoring + if (panelAnchorVerticalCenter) { + return Math.round((panelWindow.height - panelBackground.height) / 2) + } else if (panelAnchorTop) { + return marginTop + } else if (panelAnchorBottom) { + return Math.round(panelWindow.height - panelBackground.height - marginBottom) + } + + // No fixed anchoring + if (isVertical) { + // Vertical bar + if (useButtonPosition) { + // Position panel relative to button + var targetY = buttonPosition.y + (buttonHeight / 2) - (panelBackground.height / 2) + // Keep panel within screen bounds + var maxY = panelWindow.height - panelBackground.height - marginBottom + var minY = marginTop + return Math.round(Math.max(minY, Math.min(targetY, maxY))) + } else { + // Fallback to center vertically + return Math.round((panelWindow.height - panelBackground.height) / 2) + } + } else { + // Horizontal bar + if (barPosition === "bottom") { + // Above the bottom bar + return Math.round(panelWindow.height - panelBackground.height - marginBottom) + } else { + // Below the top bar + return marginTop + } } } diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 4b6a7f5..cd1d42c 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -173,6 +173,7 @@ RowLayout { NText { anchors.centerIn: parent text: root.prefix + spinBox.value + root.suffix + font.family: Settings.data.ui.fontFixed font.pointSize: Style.fontSizeM * scaling font.weight: Style.fontWeightMedium color: Color.mOnSurface diff --git a/Widgets/NWidgetLoader.qml b/Widgets/NWidgetLoader.qml index 17bf887..ac4c542 100644 --- a/Widgets/NWidgetLoader.qml +++ b/Widgets/NWidgetLoader.qml @@ -49,7 +49,7 @@ Item { item.onLoaded() } - //Logger.log("NWidgetLoader", "Loaded", widgetId, "on screen", item.screen.name) + Logger.log("NWidgetLoader", "Loaded", widgetId, "on screen", item.screen.name) } }