:Merge tag 'v2.11.0'

This commit is contained in:
Never Gude 2025-09-18 22:47:16 +02:00
commit 67fed691d1
54 changed files with 776 additions and 794 deletions

Binary file not shown.

View file

@ -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 { JsonAdapter {
id: adapter id: adapter
property int settingsVersion: 2 property int settingsVersion: 3
// bar // bar
property JsonObject bar: JsonObject { property JsonObject bar: JsonObject {
property string position: "top" // "top", "bottom", "left", or "right" property string position: "top" // "top", "bottom", "left", or "right"
property real backgroundOpacity: 1.0 property real backgroundOpacity: 1.0
property list<string> monitors: [] property list<string> monitors: []
property string density: "default" // "compact", "default", "comfortable"
property bool showCapsule: true
// Floating bar settings // Floating bar settings
property bool floating: false property bool floating: false
@ -320,7 +328,7 @@ Singleton {
// general // general
property JsonObject general: JsonObject { property JsonObject general: JsonObject {
property string avatarImage: defaultAvatar property string avatarImage: defaultAvatar
property bool dimDesktop: false property bool dimDesktop: true
property bool showScreenCorners: false property bool showScreenCorners: false
property bool forceBlackScreenCorners: false property bool forceBlackScreenCorners: false
property real radiusRatio: 1.0 property real radiusRatio: 1.0

View file

@ -65,14 +65,36 @@ Singleton {
property int animationSlow: Math.round(450 / Settings.data.general.animationSpeed) property int animationSlow: Math.round(450 / Settings.data.general.animationSpeed)
property int animationSlowest: Math.round(750 / 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 // Delays
property int tooltipDelay: 300 property int tooltipDelay: 300
property int tooltipDelayLong: 1200 property int tooltipDelayLong: 1200
property int pillDelay: 500 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
}
}
} }

View file

@ -68,23 +68,12 @@ Variants {
radius: Settings.data.bar.floating ? Style.radiusL : 0 radius: Settings.data.bar.floating ? Style.radiusL : 0
} }
// For vertical bars, use a single column layout
Loader { Loader {
id: verticalBarLayout
anchors.fill: parent anchors.fill: parent
visible: Settings.data.bar.position === "left" || Settings.data.bar.position === "right" sourceComponent: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") ? verticalBarComponent : horizontalBarComponent
sourceComponent: verticalBarComponent
} }
// For horizontal bars, use the original three-section layout // For vertical bars
Loader {
id: horizontalBarLayout
anchors.fill: parent
visible: Settings.data.bar.position === "top" || Settings.data.bar.position === "bottom"
sourceComponent: horizontalBarComponent
}
// Main layout components
Component { Component {
id: verticalBarComponent id: verticalBarComponent
Item { Item {
@ -163,6 +152,7 @@ Variants {
} }
} }
// For horizontal bars
Component { Component {
id: horizontalBarComponent id: horizontalBarComponent
Item { Item {

View file

@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import qs.Commons import qs.Commons
import qs.Services import qs.Services
import qs.Widgets
Item { Item {
id: root id: root
@ -10,13 +11,13 @@ Item {
property string text: "" property string text: ""
property string suffix: "" property string suffix: ""
property string tooltipText: "" property string tooltipText: ""
property real sizeRatio: 0.8
property bool autoHide: false property bool autoHide: false
property bool forceOpen: false property bool forceOpen: false
property bool forceClose: false property bool forceClose: false
property bool disableOpen: false property bool disableOpen: false
property bool rightOpen: false property bool rightOpen: false
property bool hovered: false property bool hovered: false
property bool compact: false
readonly property string barPosition: Settings.data.bar.position readonly property string barPosition: Settings.data.bar.position
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right" readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
@ -41,18 +42,18 @@ Item {
Component { Component {
id: verticalPillComponent id: verticalPillComponent
NPillVertical { BarPillVertical {
icon: root.icon icon: root.icon
text: root.text text: root.text
suffix: root.suffix suffix: root.suffix
tooltipText: root.tooltipText tooltipText: root.tooltipText
sizeRatio: root.sizeRatio
autoHide: root.autoHide autoHide: root.autoHide
forceOpen: root.forceOpen forceOpen: root.forceOpen
forceClose: root.forceClose forceClose: root.forceClose
disableOpen: root.disableOpen disableOpen: root.disableOpen
rightOpen: root.rightOpen rightOpen: root.rightOpen
hovered: root.hovered hovered: root.hovered
compact: root.compact
onShown: root.shown() onShown: root.shown()
onHidden: root.hidden() onHidden: root.hidden()
onEntered: root.entered() onEntered: root.entered()
@ -66,18 +67,18 @@ Item {
Component { Component {
id: horizontalPillComponent id: horizontalPillComponent
NPillHorizontal { BarPillHorizontal {
icon: root.icon icon: root.icon
text: root.text text: root.text
suffix: root.suffix suffix: root.suffix
tooltipText: root.tooltipText tooltipText: root.tooltipText
sizeRatio: root.sizeRatio
autoHide: root.autoHide autoHide: root.autoHide
forceOpen: root.forceOpen forceOpen: root.forceOpen
forceClose: root.forceClose forceClose: root.forceClose
disableOpen: root.disableOpen disableOpen: root.disableOpen
rightOpen: root.rightOpen rightOpen: root.rightOpen
hovered: root.hovered hovered: root.hovered
compact: root.compact
onShown: root.shown() onShown: root.shown()
onHidden: root.hidden() onHidden: root.hidden()
onEntered: root.entered() onEntered: root.entered()

View file

@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import qs.Commons import qs.Commons
import qs.Services import qs.Services
import qs.Widgets
Item { Item {
id: root id: root
@ -10,13 +11,13 @@ Item {
property string text: "" property string text: ""
property string suffix: "" property string suffix: ""
property string tooltipText: "" property string tooltipText: ""
property real sizeRatio: 0.8
property bool autoHide: false property bool autoHide: false
property bool forceOpen: false property bool forceOpen: false
property bool forceClose: false property bool forceClose: false
property bool disableOpen: false property bool disableOpen: false
property bool rightOpen: false property bool rightOpen: false
property bool hovered: false property bool hovered: false
property bool compact: false
// Effective shown state (true if hovered/animated open or forced) // Effective shown state (true if hovered/animated open or forced)
readonly property bool revealed: forceOpen || showPill readonly property bool revealed: forceOpen || showPill
@ -34,26 +35,27 @@ Item {
property bool showPill: false property bool showPill: false
property bool shouldAnimateHide: false property bool shouldAnimateHide: false
// Exposed width logic readonly property int pillHeight: Math.round(Style.capsuleHeight * scaling)
readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling) readonly property int pillPaddingHorizontal: Math.round(Style.capsuleHeight * 0.2 * scaling)
readonly property int pillHeight: iconSize readonly property int pillOverlap: Math.round(Style.capsuleHeight * 0.5 * scaling)
readonly property int pillPaddingHorizontal: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin readonly property int pillMaxWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap)
readonly property int pillOverlap: iconSize * 0.5
readonly property int maxPillWidth: 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 height: pillHeight
Rectangle { Rectangle {
id: pill id: pill
width: revealed ? maxPillWidth : 1 width: revealed ? pillMaxWidth : 1
height: pillHeight height: pillHeight
x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right
(iconCircle.x + iconCircle.width / 2) - width // Opens left (iconCircle.x + iconCircle.width / 2) - width // Opens left
opacity: revealed ? Style.opacityFull : Style.opacityNone opacity: revealed ? Style.opacityFull : Style.opacityNone
color: Color.mSurfaceVariant color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
topLeftRadius: rightOpen ? 0 : pillHeight * 0.5 topLeftRadius: rightOpen ? 0 : pillHeight * 0.5
bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5 bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5
@ -76,7 +78,7 @@ Item {
} }
text: root.text + root.suffix text: root.text + root.suffix
font.family: Settings.data.ui.fontFixed font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXS * scaling font.pointSize: textSize
font.weight: Style.fontWeightBold font.weight: Style.fontWeightBold
color: forceOpen ? Color.mOnSurface : Color.mPrimary color: forceOpen ? Color.mOnSurface : Color.mPrimary
visible: revealed visible: revealed
@ -100,10 +102,10 @@ Item {
Rectangle { Rectangle {
id: iconCircle id: iconCircle
width: iconSize width: pillHeight
height: iconSize height: pillHeight
radius: width * 0.5 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 anchors.verticalCenter: parent.verticalCenter
x: rightOpen ? 0 : (parent.width - width) x: rightOpen ? 0 : (parent.width - width)
@ -117,7 +119,7 @@ Item {
NIcon { NIcon {
icon: root.icon icon: root.icon
font.pointSize: Style.fontSizeM * scaling font.pointSize: iconSize
color: hovered ? Color.mOnTertiary : Color.mOnSurface color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally // Center horizontally
x: (iconCircle.width - width) / 2 x: (iconCircle.width - width) / 2
@ -133,7 +135,7 @@ Item {
target: pill target: pill
property: "width" property: "width"
from: 1 from: 1
to: maxPillWidth to: pillMaxWidth
duration: Style.animationNormal duration: Style.animationNormal
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
@ -173,7 +175,7 @@ Item {
NumberAnimation { NumberAnimation {
target: pill target: pill
property: "width" property: "width"
from: maxPillWidth from: pillMaxWidth
to: 1 to: 1
duration: Style.animationNormal duration: Style.animationNormal
easing.type: Easing.InCubic easing.type: Easing.InCubic

View file

@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import qs.Commons import qs.Commons
import qs.Services import qs.Services
import qs.Widgets
Item { Item {
id: root id: root
@ -10,13 +11,13 @@ Item {
property string text: "" property string text: ""
property string suffix: "" property string suffix: ""
property string tooltipText: "" property string tooltipText: ""
property real sizeRatio: 0.8
property bool autoHide: false property bool autoHide: false
property bool forceOpen: false property bool forceOpen: false
property bool forceClose: false property bool forceClose: false
property bool disableOpen: false property bool disableOpen: false
property bool rightOpen: false property bool rightOpen: false
property bool hovered: false property bool hovered: false
property bool compact: false
// Bar position detection for pill direction // Bar position detection for pill direction
readonly property string barPosition: Settings.data.bar.position readonly property string barPosition: Settings.data.bar.position
@ -43,16 +44,19 @@ Item {
property bool shouldAnimateHide: false property bool shouldAnimateHide: false
// Sizing logic for vertical bars // Sizing logic for vertical bars
readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling) readonly property int buttonSize: Math.round(Style.capsuleHeight * scaling)
readonly property int pillHeight: iconSize readonly property int pillHeight: buttonSize
readonly property int pillPaddingVertical: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin 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 pillOverlap: buttonSize * 0.5
readonly property int maxPillWidth: iconSize readonly property int maxPillWidth: buttonSize
readonly property int maxPillHeight: Math.max(1, textItem.implicitHeight + pillPaddingVertical * 4) 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 // For vertical bars: width is just icon size, height includes pill space
width: iconSize width: buttonSize
height: revealed ? (iconSize + maxPillHeight - pillOverlap) : iconSize height: revealed ? (buttonSize + maxPillHeight - pillOverlap) : buttonSize
Rectangle { Rectangle {
id: pill id: pill
@ -64,13 +68,13 @@ Item {
y: openUpward ? (iconCircle.y + iconCircle.height / 2 - height) : (iconCircle.y + iconCircle.height / 2) y: openUpward ? (iconCircle.y + iconCircle.height / 2 - height) : (iconCircle.y + iconCircle.height / 2)
opacity: revealed ? Style.opacityFull : Style.opacityNone 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 // Radius logic for vertical expansion - rounded on the side that connects to icon
topLeftRadius: openUpward ? iconSize * 0.5 : 0 topLeftRadius: openUpward ? buttonSize * 0.5 : 0
bottomLeftRadius: openDownward ? iconSize * 0.5 : 0 bottomLeftRadius: openDownward ? buttonSize * 0.5 : 0
topRightRadius: openUpward ? iconSize * 0.5 : 0 topRightRadius: openUpward ? buttonSize * 0.5 : 0
bottomRightRadius: openDownward ? iconSize * 0.5 : 0 bottomRightRadius: openDownward ? buttonSize * 0.5 : 0
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@ -88,7 +92,7 @@ Item {
} }
text: root.text + root.suffix text: root.text + root.suffix
font.family: Settings.data.ui.fontFixed font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling font.pointSize: textSize
font.weight: Style.fontWeightMedium font.weight: Style.fontWeightMedium
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@ -121,10 +125,10 @@ Item {
Rectangle { Rectangle {
id: iconCircle id: iconCircle
width: iconSize width: buttonSize
height: iconSize height: buttonSize
radius: width * 0.5 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 // Icon positioning based on direction
x: 0 x: 0
@ -140,7 +144,7 @@ Item {
NIcon { NIcon {
icon: root.icon icon: root.icon
font.pointSize: Style.fontSizeM * scaling font.pointSize: iconSize
color: hovered ? Color.mOnTertiary : Color.mOnSurface color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally // Center horizontally
x: (iconCircle.width - width) / 2 x: (iconCircle.width - width) / 2

View file

@ -37,8 +37,18 @@ Item {
readonly property real maxWidth: minWidth * 2 readonly property real maxWidth: minWidth * 2
readonly property string barPosition: Settings.data.bar.position 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) 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() { function getTitle() {
try { try {
@ -60,7 +70,7 @@ Item {
let total = Style.marginM * 2 * scaling // internal padding let total = Style.marginM * 2 * scaling // internal padding
if (showIcon) { 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 // Calculate actual text width more accurately
@ -129,12 +139,14 @@ Item {
Rectangle { Rectangle {
id: windowTitleRect id: windowTitleRect
visible: root.visible 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.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : (horizontalLayout.implicitWidth + Style.marginM * 2 * scaling) 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) height: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : Math.round(Style.capsuleHeight * scaling)
radius: Math.round(Style.radiusM * scaling) radius: width / 2
color: Color.mSurfaceVariant color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
Item { Item {
id: mainContainer id: mainContainer
@ -152,8 +164,8 @@ Item {
// Window icon // Window icon
Item { Item {
Layout.preferredWidth: Style.baseWidgetSize * 0.5 * scaling Layout.preferredWidth: Style.capsuleHeight * 0.75 * scaling
Layout.preferredHeight: Style.baseWidgetSize * 0.5 * scaling Layout.preferredHeight: Style.capsuleHeight * 0.75 * scaling
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
visible: getTitle() !== "" && showIcon visible: getTitle() !== "" && showIcon
@ -217,10 +229,9 @@ Item {
// Window icon // Window icon
Item { Item {
width: Style.baseWidgetSize * 0.5 * scaling width: Style.capsuleHeight * 0.75 * scaling
height: Style.baseWidgetSize * 0.5 * scaling height: Style.capsuleHeight * 0.75 * scaling
anchors.centerIn: parent anchors.centerIn: parent
visible: getTitle() !== "" && showIcon
IconImage { IconImage {
id: windowIconVertical id: windowIconVertical

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import qs.Commons import qs.Commons
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.Bar.Extras
Item { Item {
id: root id: root
@ -81,10 +82,11 @@ Item {
} }
} }
NPill { BarPill {
id: pill 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) icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent, charging, isReady)
text: (isReady || testMode) ? Math.round(percent) : "-" text: (isReady || testMode) ? Math.round(percent) : "-"
suffix: "%" suffix: "%"

View file

@ -13,8 +13,9 @@ NIconButton {
property ShellScreen screen property ShellScreen screen
property real scaling: 1.0 property real scaling: 1.0
sizeRatio: 0.8 baseSize: Style.capsuleHeight
colorBg: Color.mSurfaceVariant compact: (Settings.data.bar.density === "compact")
colorBg: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
colorFg: Color.mOnSurface colorFg: Color.mOnSurface
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent

View file

@ -4,6 +4,7 @@ import qs.Commons
import qs.Modules.SettingsPanel import qs.Modules.SettingsPanel
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.Bar.Extras
Item { Item {
id: root id: root
@ -73,10 +74,11 @@ Item {
onTriggered: pill.hide() onTriggered: pill.hide()
} }
NPill { BarPill {
id: pill id: pill
rightOpen: BarWidgetRegistry.getNPillDirection(root) compact: (Settings.data.bar.density === "compact")
rightOpen: BarWidgetRegistry.getPillDirection(root)
icon: getIcon() icon: getIcon()
autoHide: false // Important to be false so we can hover as long as we want autoHide: false // Important to be false so we can hover as long as we want
text: { text: {

View file

@ -29,6 +29,7 @@ Rectangle {
} }
readonly property string barPosition: Settings.data.bar.position 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 // Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property bool use12h: widgetSettings.use12HourClock !== undefined ? widgetSettings.use12HourClock : widgetMetadata.use12HourClock readonly property bool use12h: widgetSettings.use12HourClock !== undefined ? widgetSettings.use12HourClock : widgetMetadata.use12HourClock
@ -39,15 +40,15 @@ Rectangle {
readonly property bool verticalMode: barPosition === "left" || barPosition === "right" readonly property bool verticalMode: barPosition === "left" || barPosition === "right"
implicitWidth: verticalMode ? Math.round(Style.capsuleHeight * scaling) : Math.round(layout.implicitWidth + Style.marginM * 2 * scaling) 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) radius: Math.round(Style.radiusS * scaling)
color: Color.mSurfaceVariant color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
Item { Item {
id: clockContainer id: clockContainer
anchors.fill: parent anchors.fill: parent
anchors.margins: Style.marginXS * scaling anchors.margins: compact ? 0 : Style.marginXS * scaling
ColumnLayout { ColumnLayout {
id: layout id: layout

View file

@ -6,6 +6,7 @@ import qs.Commons
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.SettingsPanel import qs.Modules.SettingsPanel
import qs.Modules.Bar.Extras
Item { Item {
id: root id: root
@ -43,12 +44,13 @@ Item {
implicitWidth: pill.width implicitWidth: pill.width
implicitHeight: pill.height implicitHeight: pill.height
NPill { BarPill {
id: pill id: pill
rightOpen: BarWidgetRegistry.getNPillDirection(root) rightOpen: BarWidgetRegistry.getPillDirection(root)
icon: customIcon icon: customIcon
text: _dynamicText text: _dynamicText
compact: (Settings.data.bar.density === "compact")
autoHide: false autoHide: false
forceOpen: _dynamicText !== "" forceOpen: _dynamicText !== ""
forceClose: false forceClose: false

View file

@ -10,10 +10,10 @@ NIconButton {
property real scaling: 1.0 property real scaling: 1.0
icon: "dark-mode" icon: "dark-mode"
tooltipText: "Toggle light/dark mode" tooltipText: "Toggle light/dark mode."
sizeRatio: 0.8 compact: (Settings.data.bar.density === "compact")
baseSize: Style.capsuleHeight
colorBg: Settings.data.colorSchemes.darkMode ? Color.mSurfaceVariant : Color.mPrimary colorBg: Settings.data.colorSchemes.darkMode ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary
colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent

View file

@ -11,14 +11,12 @@ NIconButton {
property ShellScreen screen property ShellScreen screen
property real scaling: 1.0 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" icon: IdleInhibitorService.isInhibited ? "keep-awake-on" : "keep-awake-off"
tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake" : "Enable keep awake" 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 colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface
colorBorder: Color.transparent colorBorder: Color.transparent
onClicked: { onClicked: IdleInhibitorService.manualToggle()
IdleInhibitorService.manualToggle()
}
} }

View file

@ -6,6 +6,7 @@ import Quickshell.Io
import qs.Commons import qs.Commons
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.Bar.Extras
Item { Item {
id: root id: root
@ -38,11 +39,12 @@ Item {
implicitWidth: pill.width implicitWidth: pill.width
implicitHeight: pill.height implicitHeight: pill.height
NPill { BarPill {
id: pill id: pill
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
rightOpen: BarWidgetRegistry.getNPillDirection(root) compact: (Settings.data.bar.density === "compact")
rightOpen: BarWidgetRegistry.getPillDirection(root)
icon: "keyboard" icon: "keyboard"
autoHide: false // Important to be false so we can hover as long as we want autoHide: false // Important to be false so we can hover as long as we want
text: currentLayout.toUpperCase() text: currentLayout.toUpperCase()

View file

@ -31,6 +31,7 @@ Item {
} }
readonly property string barPosition: Settings.data.bar.position 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 showAlbumArt: (widgetSettings.showAlbumArt !== undefined) ? widgetSettings.showAlbumArt : widgetMetadata.showAlbumArt
readonly property bool showVisualizer: (widgetSettings.showVisualizer !== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer 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) 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) 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) 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 // Used to anchor the tooltip, so the tooltip does not move when the content expands
Item { Item {

View file

@ -6,6 +6,7 @@ import qs.Commons
import qs.Modules.SettingsPanel import qs.Modules.SettingsPanel
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.Bar.Extras
Item { Item {
id: root id: root
@ -86,10 +87,11 @@ Item {
} }
} }
NPill { BarPill {
id: pill id: pill
rightOpen: BarWidgetRegistry.getNPillDirection(root) rightOpen: BarWidgetRegistry.getPillDirection(root)
icon: getIcon() icon: getIcon()
compact: (Settings.data.bar.density === "compact")
autoHide: false // Important to be false so we can hover as long as we want autoHide: false // Important to be false so we can hover as long as we want
text: Math.floor(AudioService.inputVolume * 100) text: Math.floor(AudioService.inputVolume * 100)
suffix: "%" suffix: "%"

View file

@ -14,8 +14,9 @@ NIconButton {
property ShellScreen screen property ShellScreen screen
property real scaling: 1.0 property real scaling: 1.0
sizeRatio: 0.8 compact: (Settings.data.bar.density === "compact")
colorBg: Settings.data.nightLight.forced ? Color.mPrimary : Color.mSurfaceVariant 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 colorFg: Settings.data.nightLight.forced ? Color.mOnPrimary : Color.mOnSurface
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent

View file

@ -49,10 +49,11 @@ NIconButton {
return count return count
} }
sizeRatio: 0.8 baseSize: Style.capsuleHeight
compact: (Settings.data.bar.density === "compact")
icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell" 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'." 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 colorFg: Color.mOnSurface
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent
@ -68,29 +69,20 @@ NIconButton {
Loader { Loader {
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
anchors.rightMargin: -4 * scaling anchors.rightMargin: 2 * scaling
anchors.topMargin: -4 * scaling anchors.topMargin: 1 * scaling
z: 2 z: 2
active: showUnreadBadge && (!hideWhenZero || computeUnreadCount() > 0) active: showUnreadBadge && (!hideWhenZero || computeUnreadCount() > 0)
sourceComponent: Rectangle { sourceComponent: Rectangle {
id: badge id: badge
readonly property int count: computeUnreadCount() readonly property int count: computeUnreadCount()
readonly property string label: count <= 99 ? String(count) : "99+" height: 8 * scaling
readonly property real pad: 8 * scaling width: height
height: 16 * scaling
width: Math.max(height, textNode.implicitWidth + pad)
radius: height / 2 radius: height / 2
color: Color.mError color: Color.mError
border.color: Color.mSurface border.color: Color.mSurface
border.width: 1 border.width: 1
visible: count > 0 || !hideWhenZero visible: count > 0 || !hideWhenZero
NText {
id: textNode
anchors.centerIn: parent
text: badge.label
font.pointSize: Style.fontSizeXXS * scaling
color: Color.mOnError
}
} }
} }
} }

View file

@ -13,7 +13,7 @@ NIconButton {
property real scaling: 1.0 property real scaling: 1.0
readonly property bool hasPP: PowerProfileService.available readonly property bool hasPP: PowerProfileService.available
sizeRatio: 0.8 baseSize: Style.capsuleHeight
visible: hasPP visible: hasPP
function profileIcon() { function profileIcon() {
@ -46,7 +46,8 @@ NIconButton {
icon: root.profileIcon() icon: root.profileIcon()
tooltipText: root.profileName() 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 colorFg: (PowerProfileService.profile === PowerProfile.Balanced) ? Color.mOnSurface : Color.mOnPrimary
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent

View file

@ -11,11 +11,11 @@ NIconButton {
property ShellScreen screen property ShellScreen screen
property real scaling: 1.0 property real scaling: 1.0
sizeRatio: 0.8 compact: (Settings.data.bar.density === "compact")
baseSize: Style.capsuleHeight
icon: "power" icon: "power"
tooltipText: "Power Settings" tooltipText: "Power Settings"
colorBg: Color.mSurfaceVariant colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent)
colorFg: Color.mError colorFg: Color.mError
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent

View file

@ -12,8 +12,9 @@ NIconButton {
visible: ScreenRecorderService.isRecording visible: ScreenRecorderService.isRecording
icon: "camera-video" icon: "camera-video"
tooltipText: "Screen recording is active\nClick to stop recording" tooltipText: "Screen recording is active.\nClick to stop recording."
sizeRatio: 0.8 compact: (Settings.data.bar.density === "compact")
baseSize: Style.capsuleHeight
colorBg: Color.mPrimary colorBg: Color.mPrimary
colorFg: Color.mOnPrimary colorFg: Color.mOnPrimary
onClicked: ScreenRecorderService.toggleRecording() onClicked: ScreenRecorderService.toggleRecording()

View file

@ -33,9 +33,9 @@ NIconButton {
icon: useDistroLogo ? "" : "noctalia" icon: useDistroLogo ? "" : "noctalia"
tooltipText: "Open side panel." tooltipText: "Open side panel."
sizeRatio: 0.85 baseSize: Style.capsuleHeight
compact: (Settings.data.bar.density === "compact")
colorBg: Color.mSurfaceVariant colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent)
colorFg: Color.mOnSurface colorFg: Color.mOnSurface
colorBgHover: useDistroLogo ? Color.mSurfaceVariant : Color.mTertiary colorBgHover: useDistroLogo ? Color.mSurfaceVariant : Color.mTertiary
colorBorder: Color.transparent colorBorder: Color.transparent
@ -46,10 +46,11 @@ NIconButton {
IconImage { IconImage {
id: logo id: logo
anchors.centerIn: parent anchors.centerIn: parent
width: root.width * 0.85 width: root.width * 0.8
height: width height: width
source: useDistroLogo ? DistroLogoService.osLogo : "" source: useDistroLogo ? DistroLogoService.osLogo : ""
visible: useDistroLogo && source !== "" visible: useDistroLogo && source !== ""
smooth: true smooth: true
asynchronous: true
} }
} }

View file

@ -29,6 +29,8 @@ Rectangle {
} }
readonly property string barPosition: Settings.data.bar.position 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 showCpuUsage: (widgetSettings.showCpuUsage !== undefined) ? widgetSettings.showCpuUsage : widgetMetadata.showCpuUsage
readonly property bool showCpuTemp: (widgetSettings.showCpuTemp !== undefined) ? widgetSettings.showCpuTemp : widgetMetadata.showCpuTemp 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 showNetworkStats: (widgetSettings.showNetworkStats !== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats
readonly property bool showDiskUsage: (widgetSettings.showDiskUsage !== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage 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 anchors.centerIn: parent
implicitWidth: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : Math.round(horizontalLayout.implicitWidth + Style.marginM * 2 * scaling) implicitWidth: isVertical ? Math.round(Style.capsuleHeight * scaling) : Math.round(mainGrid.implicitWidth + Style.marginM * 2 * scaling)
implicitHeight: (barPosition === "left" || barPosition === "right") ? Math.round(verticalLayout.implicitHeight + Style.marginM * 2 * scaling) : Math.round(Style.capsuleHeight * scaling) implicitHeight: isVertical ? Math.round(mainGrid.implicitHeight + Style.marginM * 2 * scaling) : Math.round(Style.capsuleHeight * scaling)
radius: Math.round(Style.radiusM * 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 GridLayout {
function formatCompactSpeed(bytesPerSecond) { id: mainGrid
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
anchors.centerIn: parent anchors.centerIn: parent
anchors.leftMargin: Style.marginM * scaling
anchors.rightMargin: Style.marginM * scaling // Dynamic layout based on bar orientation
spacing: Style.marginXS * scaling flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight
visible: barPosition === "top" || barPosition === "bottom" 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 // CPU Usage Component
Item { Item {
Layout.preferredWidth: cpuUsageRow.implicitWidth Layout.preferredWidth: cpuUsageContent.implicitWidth
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
Layout.alignment: Qt.AlignVCenter Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter
visible: showCpuUsage visible: showCpuUsage
RowLayout { GridLayout {
id: cpuUsageRow id: cpuUsageContent
anchors.centerIn: parent 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 { NIcon {
icon: "cpu-usage" icon: "cpu-usage"
font.pointSize: Style.fontSizeM * scaling font.pointSize: iconSize
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignCenter
} Layout.row: isVertical ? 1 : 0
Layout.column: 0
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
} }
} }
} }
// CPU Temperature Component // CPU Temperature Component
Item { Item {
Layout.preferredWidth: cpuTempRow.implicitWidth Layout.preferredWidth: cpuTempContent.implicitWidth
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
Layout.alignment: Qt.AlignVCenter Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter
visible: showCpuTemp visible: showCpuTemp
RowLayout { GridLayout {
id: cpuTempRow id: cpuTempContent
anchors.centerIn: parent 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 { NIcon {
icon: "cpu-temperature" icon: "cpu-temperature"
// Fire is so tall, we need to make it smaller font.pointSize: iconSize
font.pointSize: Style.fontSizeS * scaling Layout.alignment: Qt.AlignCenter
Layout.alignment: Qt.AlignVCenter Layout.row: isVertical ? 1 : 0
} Layout.column: 0
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
} }
} }
} }
// Memory Usage Component // Memory Usage Component
Item { Item {
Layout.preferredWidth: memoryUsageRow.implicitWidth Layout.preferredWidth: memoryContent.implicitWidth
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
Layout.alignment: Qt.AlignVCenter Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter
visible: showMemoryUsage visible: showMemoryUsage
RowLayout { GridLayout {
id: memoryUsageRow id: memoryContent
anchors.centerIn: parent 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 { NIcon {
icon: "memory" icon: "memory"
font.pointSize: Style.fontSizeM * scaling font.pointSize: iconSize
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignCenter
} Layout.row: isVertical ? 1 : 0
Layout.column: 0
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
} }
} }
} }
// Network Download Speed Component // Network Download Speed Component
Item { Item {
Layout.preferredWidth: networkDownloadRow.implicitWidth Layout.preferredWidth: downloadContent.implicitWidth
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
Layout.alignment: Qt.AlignVCenter Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter
visible: showNetworkStats visible: showNetworkStats
RowLayout { GridLayout {
id: networkDownloadRow id: downloadContent
anchors.centerIn: parent 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 { NIcon {
icon: "download-speed" icon: "download-speed"
font.pointSize: Style.fontSizeM * scaling font.pointSize: iconSize
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignCenter
} Layout.row: isVertical ? 1 : 0
Layout.column: 0
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
} }
} }
} }
// Network Upload Speed Component // Network Upload Speed Component
Item { Item {
Layout.preferredWidth: networkUploadRow.implicitWidth Layout.preferredWidth: uploadContent.implicitWidth
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
Layout.alignment: Qt.AlignVCenter Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter
visible: showNetworkStats visible: showNetworkStats
RowLayout { GridLayout {
id: networkUploadRow id: uploadContent
anchors.centerIn: parent 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 { NIcon {
icon: "upload-speed" icon: "upload-speed"
font.pointSize: Style.fontSizeM * scaling font.pointSize: iconSize
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignCenter
} Layout.row: isVertical ? 1 : 0
Layout.column: 0
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
} }
} }
} }
// Disk Usage Component (primary drive) // Disk Usage Component (primary drive)
Item { Item {
Layout.preferredWidth: diskUsageRow.implicitWidth Layout.preferredWidth: diskContent.implicitWidth
Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling)
Layout.alignment: Qt.AlignVCenter Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter
visible: showDiskUsage visible: showDiskUsage
RowLayout { GridLayout {
id: diskUsageRow id: diskContent
anchors.centerIn: parent anchors.centerIn: parent
spacing: Style.marginXS * scaling flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight
rows: isVertical ? 2 : 1
NIcon { columns: isVertical ? 1 : 2
icon: "storage" rowSpacing: Style.marginXXS * scaling
font.pointSize: Style.fontSizeM * scaling columnSpacing: isVertical ? (Style.marginXXS * scaling) : (Style.marginXS * scaling)
Layout.alignment: Qt.AlignVCenter
}
NText { NText {
text: `${SystemStatService.diskPercent}%` text: `${SystemStatService.diskPercent}%`
font.family: Settings.data.ui.fontFixed font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXS * scaling font.pointSize: textSize
font.weight: Style.fontWeightMedium font.weight: Style.fontWeightMedium
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: Color.mPrimary color: Color.mPrimary
} Layout.row: isVertical ? 0 : 0
} Layout.column: isVertical ? 0 : 1
}
}
// 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
} }
NIcon { NIcon {
icon: "storage" icon: "storage"
font.pointSize: Style.fontSizeS * scaling font.pointSize: iconSize
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignCenter
Layout.row: isVertical ? 1 : 0
Layout.column: 0
} }
} }
} }

View file

@ -1,5 +1,3 @@
pragma ComponentBehavior
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
@ -15,18 +13,32 @@ Rectangle {
property ShellScreen screen property ShellScreen screen
property real scaling: 1.0 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 // Always visible when there are toplevels
implicitWidth: taskbarLayout.implicitWidth + Style.marginM * scaling * 2 implicitWidth: isVerticalBar ? Math.round(Style.capsuleHeight * scaling) : taskbarLayout.implicitWidth + Style.marginM * scaling * 2
implicitHeight: Math.round(Style.capsuleHeight * scaling) implicitHeight: isVerticalBar ? taskbarLayout.implicitHeight + Style.marginM * scaling * 2 : Math.round(Style.capsuleHeight * scaling)
radius: Math.round(Style.radiusM * scaling) radius: Math.round(Style.radiusM * scaling)
color: Color.mSurfaceVariant color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
RowLayout { GridLayout {
id: taskbarLayout id: taskbarLayout
anchors.centerIn: parent anchors.fill: parent
spacing: Style.marginXXS * root.scaling 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 { Repeater {
model: ToplevelManager && ToplevelManager.toplevels ? ToplevelManager.toplevels : [] model: ToplevelManager && ToplevelManager.toplevels ? ToplevelManager.toplevels : []
@ -43,8 +55,8 @@ Rectangle {
Rectangle { Rectangle {
id: iconBackground id: iconBackground
anchors.centerIn: parent anchors.centerIn: parent
width: root.itemSize * 0.75 width: parent.width
height: root.itemSize * 0.75 height: parent.height
color: taskbarItem.isActive ? Color.mPrimary : root.color color: taskbarItem.isActive ? Color.mPrimary : root.color
border.width: 0 border.width: 0
radius: Math.round(Style.radiusXS * root.scaling) radius: Math.round(Style.radiusXS * root.scaling)
@ -54,10 +66,11 @@ Rectangle {
IconImage { IconImage {
id: appIcon id: appIcon
anchors.centerIn: parent anchors.centerIn: parent
width: Style.marginL * root.scaling width: parent.width
height: Style.marginL * root.scaling height: parent.height
source: AppIcons.iconForAppId(taskbarItem.modelData.appId) source: AppIcons.iconForAppId(taskbarItem.modelData.appId)
smooth: true smooth: true
asynchronous: true
} }
} }

View file

@ -16,9 +16,10 @@ Rectangle {
property ShellScreen screen property ShellScreen screen
property real scaling: 1.0 property real scaling: 1.0
readonly property real itemSize: 24 * scaling
readonly property string barPosition: Settings.data.bar.position readonly property string barPosition: Settings.data.bar.position
readonly property bool isVertical: barPosition === "left" || barPosition === "right" 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() { function onLoaded() {
// When the widget is fully initialized with its props set the screen for the trayMenu // 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) 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) implicitHeight: isVertical ? (trayFlow.implicitHeight + Style.marginS * scaling * 2) : Math.round(Style.capsuleHeight * scaling)
radius: Math.round(Style.radiusM * scaling) radius: Math.round(Style.radiusM * scaling)
color: Color.mSurfaceVariant color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter

View file

@ -6,6 +6,7 @@ import qs.Commons
import qs.Modules.SettingsPanel import qs.Modules.SettingsPanel
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.Bar.Extras
Item { Item {
id: root id: root
@ -71,10 +72,11 @@ Item {
} }
} }
NPill { BarPill {
id: pill id: pill
rightOpen: BarWidgetRegistry.getNPillDirection(root) compact: (Settings.data.bar.density === "compact")
rightOpen: BarWidgetRegistry.getPillDirection(root)
icon: getIcon() icon: getIcon()
autoHide: false // Important to be false so we can hover as long as we want autoHide: false // Important to be false so we can hover as long as we want
text: Math.floor(AudioService.volume * 100) text: Math.floor(AudioService.volume * 100)

View file

@ -13,9 +13,9 @@ NIconButton {
property ShellScreen screen property ShellScreen screen
property real scaling: 1.0 property real scaling: 1.0
sizeRatio: 0.8 compact: (Settings.data.bar.density === "compact")
baseSize: Style.capsuleHeight
colorBg: Color.mSurfaceVariant colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent)
colorFg: Color.mOnSurface colorFg: Color.mOnSurface
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent

View file

@ -32,6 +32,15 @@ Item {
} }
readonly property string barPosition: Settings.data.bar.position 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 string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode
readonly property bool hideUnoccupied: (widgetSettings.hideUnoccupied !== undefined) ? widgetSettings.hideUnoccupied : widgetMetadata.hideUnoccupied readonly property bool hideUnoccupied: (widgetSettings.hideUnoccupied !== undefined) ? widgetSettings.hideUnoccupied : widgetMetadata.hideUnoccupied
@ -49,47 +58,45 @@ Item {
signal workspaceChanged(int workspaceId, color accentColor) signal workspaceChanged(int workspaceId, color accentColor)
implicitHeight: (barPosition === "left" || barPosition === "right") ? calculatedVerticalHeight() : Math.round(Style.barHeight * scaling) implicitWidth: isVertical ? Math.round(Style.barHeight * scaling) : computeWidth()
implicitWidth: (barPosition === "left" || barPosition === "right") ? Math.round(Style.barHeight * scaling) : calculatedHorizontalWidth() implicitHeight: isVertical ? computeHeight() : Math.round(Style.barHeight * scaling)
function calculatedWsWidth(ws) { function getWorkspaceWidth(ws) {
const d = Style.capsuleHeight * root.baseDimensionRatio
if (ws.isFocused) if (ws.isFocused)
return Math.round(44 * scaling) return d * 2.5
else if (ws.isActive)
return Math.round(28 * scaling)
else else
return Math.round(20 * scaling) return d
} }
function calculatedWsHeight(ws) { function getWorkspaceHeight(ws) {
const d = Style.capsuleHeight * root.baseDimensionRatio
if (ws.isFocused) if (ws.isFocused)
return Math.round(44 * scaling) return d * 3
else if (ws.isActive)
return Math.round(28 * scaling)
else else
return Math.round(20 * scaling) return d
} }
function calculatedVerticalHeight() { function computeWidth() {
let total = 0 let total = 0
for (var i = 0; i < localWorkspaces.count; i++) { for (var i = 0; i < localWorkspaces.count; i++) {
const ws = localWorkspaces.get(i) const ws = localWorkspaces.get(i)
total += calculatedWsHeight(ws) total += getWorkspaceWidth(ws)
} }
total += Math.max(localWorkspaces.count - 1, 0) * spacingBetweenPills total += Math.max(localWorkspaces.count - 1, 0) * spacingBetweenPills
total += horizontalPadding * 2 total += horizontalPadding * 2
return total return Math.round(total)
} }
function calculatedHorizontalWidth() { function computeHeight() {
let total = 0 let total = 0
for (var i = 0; i < localWorkspaces.count; i++) { for (var i = 0; i < localWorkspaces.count; i++) {
const ws = localWorkspaces.get(i) const ws = localWorkspaces.get(i)
total += calculatedWsWidth(ws) total += getWorkspaceHeight(ws)
} }
total += Math.max(localWorkspaces.count - 1, 0) * spacingBetweenPills total += Math.max(localWorkspaces.count - 1, 0) * spacingBetweenPills
total += horizontalPadding * 2 total += horizontalPadding * 2
return total return Math.round(total)
} }
Component.onCompleted: { Component.onCompleted: {
@ -173,10 +180,10 @@ Item {
Rectangle { Rectangle {
id: workspaceBackground id: workspaceBackground
width: (barPosition === "left" || barPosition === "right") ? Math.round(Style.capsuleHeight * scaling) : parent.width width: isVertical ? Math.round(Style.capsuleHeight * scaling) : parent.width
height: (barPosition === "left" || barPosition === "right") ? parent.height : Math.round(Style.capsuleHeight * scaling) height: isVertical ? parent.height : Math.round(Style.capsuleHeight * scaling)
radius: Math.round(Style.radiusM * scaling) radius: Math.round(Style.radiusM * scaling)
color: Color.mSurfaceVariant color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -187,17 +194,16 @@ Item {
id: pillRow id: pillRow
spacing: spacingBetweenPills spacing: spacingBetweenPills
anchors.verticalCenter: workspaceBackground.verticalCenter anchors.verticalCenter: workspaceBackground.verticalCenter
width: root.width - horizontalPadding * 2
x: horizontalPadding x: horizontalPadding
visible: barPosition === "top" || barPosition === "bottom" visible: !isVertical
Repeater { Repeater {
id: workspaceRepeaterHorizontal id: workspaceRepeaterHorizontal
model: localWorkspaces model: localWorkspaces
Item { Item {
id: workspacePillContainer id: workspacePillContainer
height: (labelMode !== "none") ? Math.round(18 * scaling) : Math.round(14 * scaling) width: root.getWorkspaceWidth(model)
width: root.calculatedWsWidth(model) height: Style.capsuleHeight * root.baseDimensionRatio
Rectangle { Rectangle {
id: pill id: pill
@ -216,7 +222,7 @@ Item {
return model.idx.toString() 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.capitalization: Font.AllUppercase
font.family: Settings.data.ui.fontFixed font.family: Settings.data.ui.fontFixed
font.weight: Style.fontWeightBold font.weight: Style.fontWeightBold
@ -332,17 +338,16 @@ Item {
id: pillColumn id: pillColumn
spacing: spacingBetweenPills spacing: spacingBetweenPills
anchors.horizontalCenter: workspaceBackground.horizontalCenter anchors.horizontalCenter: workspaceBackground.horizontalCenter
height: root.height - horizontalPadding * 2
y: horizontalPadding y: horizontalPadding
visible: barPosition === "left" || barPosition === "right" visible: isVertical
Repeater { Repeater {
id: workspaceRepeaterVertical id: workspaceRepeaterVertical
model: localWorkspaces model: localWorkspaces
Item { Item {
id: workspacePillContainerVertical id: workspacePillContainerVertical
width: (labelMode !== "none") ? Math.round(18 * scaling) : Math.round(14 * scaling) width: Style.capsuleHeight * root.baseDimensionRatio
height: root.calculatedWsHeight(model) height: root.getWorkspaceHeight(model)
Rectangle { Rectangle {
id: pillVertical id: pillVertical
@ -361,7 +366,7 @@ Item {
return model.idx.toString() 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.capitalization: Font.AllUppercase
font.family: Settings.data.ui.fontFixed font.family: Settings.data.ui.fontFixed
font.weight: Style.fontWeightBold font.weight: Style.fontWeightBold

View file

@ -53,7 +53,7 @@ NPanel {
enabled: Settings.data.network.bluetoothEnabled enabled: Settings.data.network.bluetoothEnabled
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "refresh" icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "refresh"
tooltipText: "Refresh Devices" tooltipText: "Refresh Devices"
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
onClicked: { onClicked: {
if (BluetoothService.adapter) { if (BluetoothService.adapter) {
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
@ -64,7 +64,7 @@ NPanel {
NIconButton { NIconButton {
icon: "close" icon: "close"
tooltipText: "Close." tooltipText: "Close."
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
onClicked: { onClicked: {
root.close() root.close()
} }

View file

@ -12,7 +12,6 @@ NPanel {
preferredWidth: 340 preferredWidth: 340
preferredHeight: 320 preferredHeight: 320
panelAnchorRight: Settings.data.bar.position === "right"
// Main Column // Main Column
panelContent: ColumnLayout { panelContent: ColumnLayout {

View file

@ -79,8 +79,11 @@ Item {
"icon": app.icon || "application-x-executable", "icon": app.icon || "application-x-executable",
"isImage": false, "isImage": false,
"onActivate": function () { "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) { if (Settings.data.appLauncher.useApp2Unit && app.id) {
Logger.log("ApplicationsPlugin", `Using app2unit for: ${app.id}`) Logger.log("ApplicationsPlugin", `Using app2unit for: ${app.id}`)
if (app.runInTerminal) if (app.runInTerminal)
@ -89,11 +92,9 @@ Item {
Quickshell.execDetached(["app2unit", "--"].concat(app.command)) Quickshell.execDetached(["app2unit", "--"].concat(app.command))
} else if (app.execute) { } else if (app.execute) {
app.execute() app.execute()
} else if (app.exec) { } else {
// Fallback to manual execution Logger.log("ApplicationsPlugin", `Could not launch: ${app.name}`)
Process.execute(app.exec)
} }
launcher.close()
} }
} }
} }

View file

@ -326,7 +326,7 @@ Variants {
NIconButton { NIconButton {
icon: "close" icon: "close"
tooltipText: "Close." tooltipText: "Close."
sizeRatio: 0.6 baseSize: Style.baseWidgetSize * 0.6
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: Style.marginM * scaling anchors.topMargin: Style.marginM * scaling
anchors.right: parent.right anchors.right: parent.right

View file

@ -14,7 +14,6 @@ NPanel {
preferredWidth: 380 preferredWidth: 380
preferredHeight: 500 preferredHeight: 500
panelAnchorRight: Settings.data.bar.position === "right"
panelKeyboardFocus: true panelKeyboardFocus: true
panelContent: Rectangle { panelContent: Rectangle {
@ -48,15 +47,15 @@ NPanel {
NIconButton { NIconButton {
icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell" icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell"
tooltipText: Settings.data.notifications.doNotDisturb ? "'Do Not Disturb' is enabled." : "'Do Not Disturb' is disabled." 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 onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
onRightClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb onRightClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
} }
NIconButton { NIconButton {
icon: "trash" icon: "trash"
tooltipText: "Clear history" tooltipText: "Clear history."
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
onClicked: { onClicked: {
NotificationService.clearHistory() NotificationService.clearHistory()
root.close() root.close()
@ -66,7 +65,7 @@ NPanel {
NIconButton { NIconButton {
icon: "close" icon: "close"
tooltipText: "Close." tooltipText: "Close."
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
onClicked: { onClicked: {
root.close() root.close()
} }
@ -136,7 +135,7 @@ NPanel {
width: notificationList.width width: notificationList.width
height: notificationLayout.implicitHeight + (Style.marginM * scaling * 2) height: notificationLayout.implicitHeight + (Style.marginM * scaling * 2)
radius: Style.radiusM * scaling radius: Style.radiusM * scaling
color: notificationMouseArea.containsMouse ? Color.mTertiary : Color.mSurfaceVariant color: Color.mSurfaceVariant
border.color: Qt.alpha(Color.mOutline, Style.opacityMedium) border.color: Qt.alpha(Color.mOutline, Style.opacityMedium)
border.width: Math.max(1, Style.borderS * scaling) border.width: Math.max(1, Style.borderS * scaling)
@ -169,7 +168,7 @@ NPanel {
text: (summary || "No summary").substring(0, 100) text: (summary || "No summary").substring(0, 100)
font.pointSize: Style.fontSizeM * scaling font.pointSize: Style.fontSizeM * scaling
font.weight: Font.Medium font.weight: Font.Medium
color: notificationMouseArea.containsMouse ? Color.mOnTertiary : Color.mPrimary color: Color.mPrimary
wrapMode: Text.Wrap wrapMode: Text.Wrap
Layout.fillWidth: true Layout.fillWidth: true
maximumLineCount: 2 maximumLineCount: 2
@ -179,7 +178,7 @@ NPanel {
NText { NText {
text: (body || "").substring(0, 150) text: (body || "").substring(0, 150)
font.pointSize: Style.fontSizeXS * scaling font.pointSize: Style.fontSizeXS * scaling
color: notificationMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface color: Color.mOnSurface
wrapMode: Text.Wrap wrapMode: Text.Wrap
Layout.fillWidth: true Layout.fillWidth: true
maximumLineCount: 3 maximumLineCount: 3
@ -190,7 +189,7 @@ NPanel {
NText { NText {
text: NotificationService.formatTimestamp(timestamp) text: NotificationService.formatTimestamp(timestamp)
font.pointSize: Style.fontSizeXS * scaling font.pointSize: Style.fontSizeXS * scaling
color: notificationMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface color: Color.mOnSurface
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
@ -198,8 +197,8 @@ NPanel {
// Delete button // Delete button
NIconButton { NIconButton {
icon: "trash" icon: "trash"
tooltipText: "Delete notification" tooltipText: "Delete notification."
sizeRatio: 0.7 baseSize: Style.baseWidgetSize * 0.7
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
onClicked: { onClicked: {

View file

@ -14,6 +14,8 @@ NBox {
property var widgetModel: [] property var widgetModel: []
property var availableWidgets: [] property var availableWidgets: []
readonly property real miniButtonSize: Style.baseWidgetSize * 0.65
signal addWidget(string widgetId, string section) signal addWidget(string widgetId, string section)
signal removeWidget(string section, int index) signal removeWidget(string section, int index)
signal reorderWidget(string section, int fromIndex, int toIndex) signal reorderWidget(string section, int fromIndex, int toIndex)
@ -178,7 +180,7 @@ NBox {
active: BarWidgetRegistry.widgetHasUserSettings(modelData.id) active: BarWidgetRegistry.widgetHasUserSettings(modelData.id)
sourceComponent: NIconButton { sourceComponent: NIconButton {
icon: "settings" icon: "settings"
sizeRatio: 0.6 baseSize: miniButtonSize
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight) colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
colorBg: Color.mOnSurface colorBg: Color.mOnSurface
colorFg: Color.mOnPrimary colorFg: Color.mOnPrimary
@ -218,7 +220,7 @@ NBox {
NIconButton { NIconButton {
icon: "close" icon: "close"
sizeRatio: 0.6 baseSize: miniButtonSize
colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight) colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight)
colorBg: Color.mOnSurface colorBg: Color.mOnSurface
colorFg: Color.mOnPrimary colorFg: Color.mOnPrimary

View file

@ -298,7 +298,7 @@ ColumnLayout {
NIconButton { NIconButton {
icon: "close" icon: "close"
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: Style.marginXS * scaling Layout.rightMargin: Style.marginXS * scaling
onClicked: { onClicked: {

View file

@ -45,7 +45,6 @@ ColumnLayout {
description: "Configure bar appearance and positioning." description: "Configure bar appearance and positioning."
} }
RowLayout {
NComboBox { NComboBox {
Layout.fillWidth: true Layout.fillWidth: true
label: "Bar Position" label: "Bar Position"
@ -71,6 +70,27 @@ ColumnLayout {
currentKey: Settings.data.bar.position currentKey: Settings.data.bar.position
onSelected: key => Settings.data.bar.position = key 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 { ColumnLayout {
@ -92,6 +112,15 @@ ColumnLayout {
text: Math.floor(Settings.data.bar.backgroundOpacity * 100) + "%" 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 { NToggle {
Layout.fillWidth: true Layout.fillWidth: true
label: "Floating Bar" label: "Floating Bar"
@ -163,40 +192,6 @@ ColumnLayout {
Layout.bottomMargin: Style.marginXL * scaling 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 // Widgets Management Section
ColumnLayout { ColumnLayout {
spacing: Style.marginXXS * scaling spacing: Style.marginXXS * scaling
@ -264,6 +259,40 @@ ColumnLayout {
Layout.bottomMargin: 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.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 // Signal functions
// --------------------------------- // ---------------------------------

View file

@ -87,19 +87,9 @@ ColumnLayout {
anchors.margins: Style.marginL * scaling anchors.margins: Style.marginL * scaling
spacing: Style.marginXXS * scaling spacing: Style.marginXXS * scaling
NText { NLabel {
text: (`${modelData.name}: ${modelData.model}` || "Unknown") label: modelData.name || "Unknown"
font.pointSize: Style.fontSizeL * scaling description: `${modelData.model} - ${modelData.width}x${modelData.height} [x:${modelData.x} y:${modelData.y}]`
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
} }
// Scale // Scale
@ -134,8 +124,8 @@ ColumnLayout {
NIconButton { NIconButton {
icon: "refresh" icon: "refresh"
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.9
tooltipText: "Reset scaling" tooltipText: "Reset scaling."
onClicked: ScalingService.setScreenScale(modelData, 1.0) onClicked: ScalingService.setScreenScale(modelData, 1.0)
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter

View file

@ -93,15 +93,15 @@ ColumnLayout {
NHeader { NHeader {
label: "Monitors Configuration" label: "Monitors Configuration"
description: "Choose which monitors should display the dock." description: "Show dock on specific monitors."
} }
Repeater { Repeater {
model: Quickshell.screens || [] model: Quickshell.screens || []
delegate: NCheckbox { delegate: NCheckbox {
Layout.fillWidth: true Layout.fillWidth: true
label: `${modelData.name || "Unknown"}${modelData.model ? `: ${modelData.model}` : ""}` label: modelData.name || "Unknown"
description: `${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})` description: `${modelData.model} - ${modelData.width}x${modelData.height} [x:${modelData.x} y:${modelData.y}]`
checked: (Settings.data.dock.monitors || []).indexOf(modelData.name) !== -1 checked: (Settings.data.dock.monitors || []).indexOf(modelData.name) !== -1
onToggled: checked => { onToggled: checked => {
if (checked) { if (checked) {

View file

@ -46,40 +46,6 @@ ColumnLayout {
Layout.bottomMargin: Style.marginXL * scaling 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 // Notification Duration Settings
ColumnLayout { ColumnLayout {
spacing: Style.marginL * scaling spacing: Style.marginL * scaling
@ -159,4 +125,38 @@ ColumnLayout {
Layout.topMargin: Style.marginXL * scaling Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: 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
}
} }

View file

@ -64,7 +64,7 @@ ColumnLayout {
// Source // Source
NComboBox { NComboBox {
label: "Video Source" 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 { model: ListModel {
ListElement { ListElement {
key: "portal" key: "portal"

View file

@ -136,7 +136,7 @@ Rectangle {
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.mOutline colorBorderHover: Color.mOutline
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
onClicked: root.hide() onClicked: root.hide()

View file

@ -57,7 +57,7 @@ NPanel {
NIconButton { NIconButton {
icon: "refresh" icon: "refresh"
tooltipText: "Refresh" tooltipText: "Refresh"
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning
onClicked: NetworkService.scan() onClicked: NetworkService.scan()
} }
@ -65,7 +65,7 @@ NPanel {
NIconButton { NIconButton {
icon: "close" icon: "close"
tooltipText: "Close." tooltipText: "Close."
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
onClicked: root.close() onClicked: root.close()
} }
} }
@ -106,7 +106,7 @@ NPanel {
NIconButton { NIconButton {
icon: "close" icon: "close"
sizeRatio: 0.6 baseSize: Style.baseWidgetSize * 0.6
onClicked: NetworkService.lastError = "" 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 visible: (modelData.existing || modelData.cached) && !modelData.connected && NetworkService.connectingTo !== modelData.ssid && NetworkService.forgettingNetwork !== modelData.ssid && NetworkService.disconnectingFrom !== modelData.ssid
icon: "trash" icon: "trash"
tooltipText: "Forget network" tooltipText: "Forget network"
sizeRatio: 0.7 baseSize: Style.baseWidgetSize * 0.8
onClicked: expandedSsid = expandedSsid === modelData.ssid ? "" : modelData.ssid onClicked: expandedSsid = expandedSsid === modelData.ssid ? "" : modelData.ssid
} }
@ -478,7 +478,7 @@ NPanel {
NIconButton { NIconButton {
icon: "close" icon: "close"
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
onClicked: { onClicked: {
passwordSsid = "" passwordSsid = ""
passwordInput = "" passwordInput = ""
@ -532,7 +532,7 @@ NPanel {
NIconButton { NIconButton {
icon: "close" icon: "close"
sizeRatio: 0.8 baseSize: Style.baseWidgetSize * 0.8
onClicked: expandedSsid = "" onClicked: expandedSsid = ""
} }
} }

View file

@ -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. 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. 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 ### Application Launcher
The launcher supports special commands for enhanced functionality: The launcher supports special commands for enhanced functionality:
@ -278,10 +295,6 @@ window-rule {
clip-to-geometry true clip-to-geometry true
} }
layer-rule {
match namespace="^quickshell-wallpaper$"
}
layer-rule { layer-rule {
match namespace="^quickshell-overview$" match namespace="^quickshell-overview$"
place-within-backdrop true place-within-backdrop true

View file

@ -207,7 +207,7 @@ Singleton {
return (widgetMetadata[id] !== undefined) && (widgetMetadata[id].allowUserSettings === true) return (widgetMetadata[id] !== undefined) && (widgetMetadata[id].allowUserSettings === true)
} }
function getNPillDirection(widget) { function getPillDirection(widget) {
try { try {
if (widget.section === "left") { if (widget.section === "left") {
return true return true

View file

@ -16,6 +16,9 @@ Singleton {
property var registeredPanels: ({}) property var registeredPanels: ({})
signal willOpen
signal willClose
// Register this panel // Register this panel
function registerPanel(panel) { function registerPanel(panel) {
registeredPanels[panel.objectName] = panel registeredPanels[panel.objectName] = panel
@ -38,6 +41,14 @@ Singleton {
openedPanel.close() openedPanel.close()
} }
openedPanel = panel openedPanel = panel
// emit signal
willOpen()
}
function willClosePanel(panel) {
// emit signal
willClose()
} }
function closedPanel(panel) { function closedPanel(panel) {

View file

@ -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 to start fetching and computing the cpu temperature
function updateCpuTemperature() { function updateCpuTemperature() {

View file

@ -8,7 +8,7 @@ Singleton {
id: root id: root
// Public properties // Public properties
property string baseVersion: "2.9.2" property string baseVersion: "2.11.0"
property bool isDevelopment: false property bool isDevelopment: false
property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}`

View file

@ -13,7 +13,7 @@ Rectangle {
signal colorSelected(color color) signal colorSelected(color color)
implicitWidth: 150 * scaling implicitWidth: 150 * scaling
implicitHeight: 40 * scaling implicitHeight: Math.round(Style.baseWidgetSize * 1.1 * scaling)
radius: Style.radiusM * scaling radius: Style.radiusM * scaling
color: Color.mSurface color: Color.mSurface
@ -40,12 +40,16 @@ Rectangle {
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Style.marginS * scaling anchors {
leftMargin: Style.marginL * scaling
rightMargin: Style.marginL * scaling
}
spacing: Style.marginS * scaling spacing: Style.marginS * scaling
// Color preview circle
Rectangle { Rectangle {
Layout.preferredWidth: 24 * scaling Layout.preferredWidth: root.height * 0.6 * scaling
Layout.preferredHeight: 24 * scaling Layout.preferredHeight: root.height * 0.6 * scaling
radius: Layout.preferredWidth * 0.5 radius: Layout.preferredWidth * 0.5
color: root.selectedColor color: root.selectedColor
border.color: Color.mOutline border.color: Color.mOutline
@ -56,11 +60,14 @@ Rectangle {
text: root.selectedColor.toString().toUpperCase() text: root.selectedColor.toString().toUpperCase()
font.family: Settings.data.ui.fontFixed font.family: Settings.data.ui.fontFixed
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
} }
NIcon { NIcon {
icon: "color-picker" icon: "color-picker"
color: Color.mOnSurfaceVariant color: Color.mOnSurfaceVariant
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
} }
} }
} }

View file

@ -7,14 +7,14 @@ import qs.Services
Rectangle { Rectangle {
id: root id: root
// Multiplier to control how large the button container is relative to Style.baseWidgetSize property real baseSize: Style.baseWidgetSize
property real sizeRatio: 1.0
property string icon property string icon
property string tooltipText property string tooltipText
property bool enabled: true property bool enabled: true
property bool allowClickWhenDisabled: false property bool allowClickWhenDisabled: false
property bool hovering: false property bool hovering: false
property bool compact: false
property color colorBg: Color.mSurfaceVariant property color colorBg: Color.mSurfaceVariant
property color colorFg: Color.mPrimary property color colorFg: Color.mPrimary
@ -29,8 +29,8 @@ Rectangle {
signal rightClicked signal rightClicked
signal middleClicked signal middleClicked
implicitWidth: Math.round(Style.baseWidgetSize * scaling * sizeRatio) implicitWidth: Math.round(baseSize * scaling)
implicitHeight: Math.round(Style.baseWidgetSize * scaling * sizeRatio) implicitHeight: Math.round(baseSize * scaling)
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
color: root.enabled && root.hovering ? colorBgHover : colorBg color: root.enabled && root.hovering ? colorBgHover : colorBg
@ -47,7 +47,7 @@ Rectangle {
NIcon { NIcon {
icon: root.icon 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 color: root.enabled && root.hovering ? colorFgHover : colorFg
// Center horizontally // Center horizontally
x: (root.width - width) / 2 x: (root.width - width) / 2

View file

@ -38,9 +38,9 @@ Loader {
readonly property real originalOpacity: 0.0 readonly property real originalOpacity: 0.0
property real scaleValue: originalScale property real scaleValue: originalScale
property real opacityValue: originalOpacity property real opacityValue: originalOpacity
property real dimmingOpacity: 0
property alias isClosing: hideTimer.running property alias isClosing: hideTimer.running
readonly property string barPosition: Settings.data.bar.position
signal opened signal opened
signal closed signal closed
@ -109,9 +109,11 @@ Loader {
// ----------------------------------------- // -----------------------------------------
function close() { function close() {
dimmingOpacity = 0
scaleValue = originalScale scaleValue = originalScale
opacityValue = originalOpacity opacityValue = originalOpacity
hideTimer.start() hideTimer.start()
PanelService.willClosePanel(root)
} }
// ----------------------------------------- // -----------------------------------------
@ -141,10 +143,16 @@ Loader {
// PanelWindow has its own screen property inherited of QsWindow // PanelWindow has its own screen property inherited of QsWindow
property real scaling: ScalingService.getScreenScale(screen) 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 string barPosition: Settings.data.bar.position
readonly property bool barAtBottom: Settings.data.bar.position === "bottom" 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 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 { Connections {
target: ScalingService target: ScalingService
@ -169,16 +177,14 @@ Loader {
visible: true visible: true
// Dim desktop if required color: Settings.data.general.dimDesktop ? Qt.alpha(Color.mShadow, dimmingOpacity) : Color.transparent
color: (root.active && !root.isClosing && Settings.data.general.dimDesktop) ? Qt.alpha(Color.mShadow, Style.opacityHeavy) : Color.transparent
WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.namespace: "noctalia-panel" WlrLayershell.namespace: "noctalia-panel"
WlrLayershell.keyboardFocus: root.panelKeyboardFocus ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: root.panelKeyboardFocus ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: Style.animationNormal duration: Style.animationSlow
} }
} }
@ -186,29 +192,6 @@ Loader {
anchors.left: true anchors.left: true
anchors.right: true anchors.right: true
anchors.bottom: 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 // Close any panel with Esc without requiring focus
Shortcut { Shortcut {
@ -225,6 +208,7 @@ Loader {
onClicked: root.close() onClicked: root.close()
} }
// The actual panel's content
Rectangle { Rectangle {
id: panelBackground id: panelBackground
color: panelBackgroundColor color: panelBackgroundColor
@ -255,130 +239,131 @@ Loader {
scale: root.scaleValue scale: root.scaleValue
opacity: root.opacityValue opacity: root.opacityValue
x: calculatedX x: calculatedX
y: calculatedY y: calculatedY
// ---------------------------------------------
// 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 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
}
}
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
}
}
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
}
}
// ---------------------------------------------
property int calculatedX: { property int calculatedX: {
var barPosition = Settings.data.bar.position // 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)
}
// Check anchor properties first, even when using button positioning // No fixed anchoring
if (!panelAnchorHorizontalCenter && panelAnchorLeft) { if (isVertical) {
return Math.round(Style.marginS * scaling) // Vertical bar
} else if (!panelAnchorHorizontalCenter && panelAnchorRight) {
// For right anchor, consider bar position
if (barPosition === "right") { if (barPosition === "right") {
// If bar is on right, position panel to the left of the bar // To the left of the right bar
var maxX = panelWindow.width - barWidth - panelBackground.width - (Style.marginS * scaling) return Math.round(panelWindow.width - panelBackground.width - marginRight)
// 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 { } else {
maxXWithSpacing -= Style.marginM * scaling // To the right of the left bar
} return marginLeft
return Math.round(maxXWithSpacing)
} else {
return Math.round(maxX)
} }
} else { } else {
// Default right positioning // Horizontal bar
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)
}
}
property int calculatedY: {
var barPosition = Settings.data.bar.position
if (root.useButtonPosition) { if (root.useButtonPosition) {
// Position panel relative to button // Position panel relative to button
var targetY = root.buttonPosition.y + (root.buttonHeight / 2) - (panelBackground.height / 2) var targetX = buttonPosition.x + (buttonWidth / 2) - (panelBackground.width / 2)
// Keep panel within screen bounds // Keep panel within screen bounds
var maxY = panelWindow.height - panelBackground.height - (Style.marginS * scaling) var maxX = panelWindow.width - panelBackground.width - marginRight
var minY = Style.marginS * scaling var minX = marginLeft
return Math.round(Math.max(minX, Math.min(targetX, maxX)))
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)
} else { } 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 // Above the bottom bar
return Math.round(panelWindow.height - panelBackground.height - (Style.marginS * scaling)) return Math.round(panelWindow.height - panelBackground.height - marginBottom)
} else {
// Below the top bar
return marginTop
}
} }
} }

View file

@ -173,6 +173,7 @@ RowLayout {
NText { NText {
anchors.centerIn: parent anchors.centerIn: parent
text: root.prefix + spinBox.value + root.suffix text: root.prefix + spinBox.value + root.suffix
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeM * scaling font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightMedium font.weight: Style.fontWeightMedium
color: Color.mOnSurface color: Color.mOnSurface

View file

@ -49,7 +49,7 @@ Item {
item.onLoaded() item.onLoaded()
} }
//Logger.log("NWidgetLoader", "Loaded", widgetId, "on screen", item.screen.name) Logger.log("NWidgetLoader", "Loaded", widgetId, "on screen", item.screen.name)
} }
} }