1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 05:55:37 -05:00

smarter distance calculation

This commit is contained in:
bbedward
2025-07-24 15:09:28 -04:00
parent 8bcac36be3
commit c6df2bc11d
10 changed files with 423 additions and 486 deletions

View File

@@ -23,15 +23,14 @@ Item {
running: true running: true
onExited: (exitCode) => { onExited: (exitCode) => {
root.cavaAvailable = exitCode === 0; root.cavaAvailable = exitCode === 0;
if (root.cavaAvailable) { if (root.cavaAvailable)
cavaProcess.running = Qt.binding(() => { cavaProcess.running = Qt.binding(() => {
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing; return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
}); });
} else { else
fallbackTimer.running = Qt.binding(() => { fallbackTimer.running = Qt.binding(() => {
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing; return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
}); });
}
} }
} }

View File

@@ -28,6 +28,7 @@ PanelWindow {
PowerProfiles.profile = profile; PowerProfiles.profile = profile;
if (PowerProfiles.profile !== profile) if (PowerProfiles.profile !== profile)
ToastService.showError("Failed to set power profile"); ToastService.showError("Failed to set power profile");
} }
visible: batteryPopupVisible visible: batteryPopupVisible
@@ -51,25 +52,24 @@ PanelWindow {
onClicked: function(mouse) { onClicked: function(mouse) {
// Only close if click is outside the content loader // Only close if click is outside the content loader
var localPos = mapToItem(contentLoader, mouse.x, mouse.y); var localPos = mapToItem(contentLoader, mouse.x, mouse.y);
if (localPos.x < 0 || localPos.x > contentLoader.width || if (localPos.x < 0 || localPos.x > contentLoader.width || localPos.y < 0 || localPos.y > contentLoader.height)
localPos.y < 0 || localPos.y > contentLoader.height) {
batteryPopupVisible = false; batteryPopupVisible = false;
}
} }
} }
Loader { Loader {
id: contentLoader id: contentLoader
asynchronous: true
active: batteryPopupVisible
readonly property real targetWidth: Math.min(380, Screen.width - Theme.spacingL * 2) readonly property real targetWidth: Math.min(380, Screen.width - Theme.spacingL * 2)
readonly property real targetHeight: Math.min(450, Screen.height - Theme.barHeight - Theme.spacingS * 2) readonly property real targetHeight: Math.min(450, Screen.height - Theme.barHeight - Theme.spacingS * 2)
asynchronous: true
active: batteryPopupVisible
width: targetWidth width: targetWidth
height: targetHeight height: targetHeight
x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL) x: Math.max(Theme.spacingL, Screen.width - targetWidth - Theme.spacingL)
y: Theme.barHeight + Theme.spacingS y: Theme.barHeight + Theme.spacingS
// GPU-accelerated scale + opacity animation // GPU-accelerated scale + opacity animation
opacity: batteryPopupVisible ? 1 : 0 opacity: batteryPopupVisible ? 1 : 0
scale: batteryPopupVisible ? 1 : 0.9 scale: batteryPopupVisible ? 1 : 0.9
@@ -80,6 +80,7 @@ PanelWindow {
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized easing.bezierCurve: Anims.emphasized
} }
} }
Behavior on scale { Behavior on scale {
@@ -88,6 +89,7 @@ PanelWindow {
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasized easing.bezierCurve: Anims.emphasized
} }
} }
sourceComponent: Rectangle { sourceComponent: Rectangle {
@@ -95,337 +97,145 @@ PanelWindow {
radius: Theme.cornerRadiusLarge radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1 border.width: 1
// Remove layer rendering for better performance // Remove layer rendering for better performance
antialiasing: true antialiasing: true
smooth: true smooth: true
ScrollView {
anchors.fill: parent
anchors.margins: Theme.spacingL
clip: true
ScrollView { Column {
anchors.fill: parent
anchors.margins: Theme.spacingL
clip: true
Column {
width: parent.width
spacing: Theme.spacingL
// Header
Row {
width: parent.width width: parent.width
spacing: Theme.spacingL
Text { // Header
text: BatteryService.batteryAvailable ? "Battery Information" : "Power Management" Row {
font.pixelSize: Theme.fontSizeLarge width: parent.width
color: Theme.surfaceText
font.weight: Font.Medium Text {
anchors.verticalCenter: parent.verticalCenter text: BatteryService.batteryAvailable ? "Battery Information" : "Power Management"
} font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - 200
height: 1
}
Rectangle {
width: 32
height: 32
radius: 16
color: closeBatteryArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
DankIcon {
anchors.centerIn: parent
name: "close"
size: Theme.iconSize - 4
color: closeBatteryArea.containsMouse ? Theme.error : Theme.surfaceText
}
MouseArea {
id: closeBatteryArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
batteryPopupVisible = false;
}
}
}
Item {
width: parent.width - 200
height: 1
} }
Rectangle { Rectangle {
width: 32
height: 32
radius: 16
color: closeBatteryArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
DankIcon {
anchors.centerIn: parent
name: "close"
size: Theme.iconSize - 4
color: closeBatteryArea.containsMouse ? Theme.error : Theme.surfaceText
}
MouseArea {
id: closeBatteryArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
batteryPopupVisible = false;
}
}
}
}
Rectangle {
width: parent.width
height: 80
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
border.color: BatteryService.isCharging ? Theme.primary : (BatteryService.isLowBattery ? Theme.error : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12))
border.width: BatteryService.isCharging || BatteryService.isLowBattery ? 2 : 1
visible: BatteryService.batteryAvailable
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingL
DankIcon {
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
size: Theme.iconSizeLarge
color: {
if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.error;
if (BatteryService.isCharging)
return Theme.primary;
return Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Row {
spacing: Theme.spacingM
Text {
text: BatteryService.batteryLevel + "%"
font.pixelSize: Theme.fontSizeLarge
color: {
if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.error;
if (BatteryService.isCharging)
return Theme.primary;
return Theme.surfaceText;
}
font.weight: Font.Bold
}
Text {
text: BatteryService.batteryStatus
font.pixelSize: Theme.fontSizeMedium
color: {
if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.error;
if (BatteryService.isCharging)
return Theme.primary;
return Theme.surfaceText;
}
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
text: {
let time = BatteryService.formatTimeRemaining();
if (time !== "Unknown")
return BatteryService.isCharging ? "Time until full: " + time : "Time remaining: " + time;
return "";
}
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: text.length > 0
}
}
}
}
// No battery info card
Rectangle {
width: parent.width
height: 80
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1
visible: !BatteryService.batteryAvailable
Row {
anchors.centerIn: parent
spacing: Theme.spacingL
DankIcon {
name: Theme.getBatteryIcon(0, false, false)
size: 36
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
Text {
text: "No Battery Detected"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: "Power profile management is available"
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
}
}
}
// Battery details
Column {
width: parent.width
spacing: Theme.spacingM
visible: BatteryService.batteryAvailable
Text {
text: "Battery Details"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Row {
width: parent.width width: parent.width
spacing: Theme.spacingXL height: 80
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
border.color: BatteryService.isCharging ? Theme.primary : (BatteryService.isLowBattery ? Theme.error : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12))
border.width: BatteryService.isCharging || BatteryService.isLowBattery ? 2 : 1
visible: BatteryService.batteryAvailable
// Health Row {
Column { anchors.left: parent.left
spacing: 2 anchors.leftMargin: Theme.spacingL
width: (parent.width - Theme.spacingXL) / 2 anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingL
Text { DankIcon {
text: "Health" name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
font.pixelSize: Theme.fontSizeSmall size: Theme.iconSizeLarge
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
font.weight: Font.Medium
}
Text {
text: BatteryService.batteryHealth
font.pixelSize: Theme.fontSizeMedium
color: { color: {
if (BatteryService.batteryHealth === "N/A") if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.surfaceText; return Theme.error;
var healthNum = parseInt(BatteryService.batteryHealth); if (BatteryService.isCharging)
return healthNum < 80 ? Theme.error : Theme.surfaceText; return Theme.primary;
return Theme.surfaceText;
} }
anchors.verticalCenter: parent.verticalCenter
} }
} Column {
spacing: 2
// Capacity anchors.verticalCenter: parent.verticalCenter
Column {
spacing: 2
width: (parent.width - Theme.spacingXL) / 2
Text {
text: "Capacity"
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
font.weight: Font.Medium
}
Text {
text: BatteryService.batteryCapacity > 0 ? BatteryService.batteryCapacity.toFixed(1) + " Wh" : "Unknown"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
}
}
}
}
// Power profiles
Column {
width: parent.width
spacing: Theme.spacingM
visible: true
Text {
text: "Power Profile"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Column {
width: parent.width
spacing: Theme.spacingS
Repeater {
model: (typeof PowerProfiles !== "undefined") ? [PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) : [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: profileArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (batteryPopout.isActiveProfile(modelData) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : "transparent"
border.width: 2
Row { Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM spacing: Theme.spacingM
DankIcon { Text {
name: Theme.getPowerProfileIcon(modelData) text: BatteryService.batteryLevel + "%"
size: Theme.iconSize font.pixelSize: Theme.fontSizeLarge
color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText color: {
anchors.verticalCenter: parent.verticalCenter if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.error;
if (BatteryService.isCharging)
return Theme.primary;
return Theme.surfaceText;
}
font.weight: Font.Bold
} }
Column { Text {
spacing: 2 text: BatteryService.batteryStatus
font.pixelSize: Theme.fontSizeMedium
color: {
if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.error;
if (BatteryService.isCharging)
return Theme.primary;
return Theme.surfaceText;
}
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
Text {
text: Theme.getPowerProfileLabel(modelData)
font.pixelSize: Theme.fontSizeMedium
color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
font.weight: batteryPopout.isActiveProfile(modelData) ? Font.Medium : Font.Normal
}
Text {
text: Theme.getPowerProfileDescription(modelData)
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
} }
} }
MouseArea { Text {
id: profileArea text: {
let time = BatteryService.formatTimeRemaining();
if (time !== "Unknown")
return BatteryService.isCharging ? "Time until full: " + time : "Time remaining: " + time;
anchors.fill: parent return "";
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
batteryPopout.setProfile(modelData);
} }
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: text.length > 0
} }
} }
@@ -434,46 +244,238 @@ PanelWindow {
} }
} // No battery info card
Rectangle {
width: parent.width
height: 80
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1
visible: !BatteryService.batteryAvailable
// Degradation reason warning Row {
Rectangle { anchors.centerIn: parent
width: parent.width spacing: Theme.spacingL
height: 60
radius: Theme.cornerRadius
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
border.color: Theme.error
border.width: 2
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
Row { DankIcon {
anchors.left: parent.left name: Theme.getBatteryIcon(0, false, false)
anchors.leftMargin: Theme.spacingL size: 36
anchors.verticalCenter: parent.verticalCenter color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
Text {
text: "No Battery Detected"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: "Power profile management is available"
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
}
}
}
// Battery details
Column {
width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingM
visible: BatteryService.batteryAvailable
DankIcon { Text {
name: "warning" text: "Battery Details"
size: Theme.iconSize font.pixelSize: Theme.fontSizeLarge
color: Theme.error color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter font.weight: Font.Medium
}
Row {
width: parent.width
spacing: Theme.spacingXL
// Health
Column {
spacing: 2
width: (parent.width - Theme.spacingXL) / 2
Text {
text: "Health"
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
font.weight: Font.Medium
}
Text {
text: BatteryService.batteryHealth
font.pixelSize: Theme.fontSizeMedium
color: {
if (BatteryService.batteryHealth === "N/A")
return Theme.surfaceText;
var healthNum = parseInt(BatteryService.batteryHealth);
return healthNum < 80 ? Theme.error : Theme.surfaceText;
}
}
}
// Capacity
Column {
spacing: 2
width: (parent.width - Theme.spacingXL) / 2
Text {
text: "Capacity"
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
font.weight: Font.Medium
}
Text {
text: BatteryService.batteryCapacity > 0 ? BatteryService.batteryCapacity.toFixed(1) + " Wh" : "Unknown"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
}
}
}
}
// Power profiles
Column {
width: parent.width
spacing: Theme.spacingM
visible: true
Text {
text: "Power Profile"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
} }
Column { Column {
spacing: 2 width: parent.width
anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingS
Repeater {
model: (typeof PowerProfiles !== "undefined") ? [PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) : [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: profileArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (batteryPopout.isActiveProfile(modelData) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : "transparent"
border.width: 2
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: Theme.getPowerProfileIcon(modelData)
size: Theme.iconSize
color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: Theme.getPowerProfileLabel(modelData)
font.pixelSize: Theme.fontSizeMedium
color: batteryPopout.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
font.weight: batteryPopout.isActiveProfile(modelData) ? Font.Medium : Font.Normal
}
Text {
text: Theme.getPowerProfileDescription(modelData)
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
}
}
MouseArea {
id: profileArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
batteryPopout.setProfile(modelData);
}
}
}
Text {
text: "Power Profile Degradation"
font.pixelSize: Theme.fontSizeMedium
color: Theme.error
font.weight: Font.Medium
} }
Text { }
text: (typeof PowerProfiles !== "undefined") ? PerformanceDegradationReason.toString(PowerProfiles.degradationReason) : ""
font.pixelSize: Theme.fontSizeSmall }
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.8)
// Degradation reason warning
Rectangle {
width: parent.width
height: 60
radius: Theme.cornerRadius
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
border.color: Theme.error
border.width: 2
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "warning"
size: Theme.iconSize
color: Theme.error
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: "Power Profile Degradation"
font.pixelSize: Theme.fontSizeMedium
color: Theme.error
font.weight: Font.Medium
}
Text {
text: (typeof PowerProfiles !== "undefined") ? PerformanceDegradationReason.toString(PowerProfiles.degradationReason) : ""
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.8)
}
} }
} }
@@ -486,9 +488,6 @@ PanelWindow {
} }
}
} }
} }

View File

@@ -6,6 +6,7 @@ Rectangle {
id: root id: root
property date currentDate: new Date() property date currentDate: new Date()
property bool compactMode: false
signal clockClicked() signal clockClicked()
@@ -36,6 +37,7 @@ Rectangle {
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: !compactMode
} }
Text { Text {
@@ -44,6 +46,7 @@ Rectangle {
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: !compactMode
} }
} }

View File

@@ -16,11 +16,9 @@ Rectangle {
height: 30 height: 30
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: cpuArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08) color: cpuArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
Component.onCompleted: { Component.onCompleted: {
SysMonitorService.addRef(); SysMonitorService.addRef();
} }
Component.onDestruction: { Component.onDestruction: {
SysMonitorService.removeRef(); SysMonitorService.removeRef();
} }
@@ -35,6 +33,7 @@ Rectangle {
SysMonitorService.setSortBy("cpu"); SysMonitorService.setSortBy("cpu");
if (root.toggleProcessList) if (root.toggleProcessList)
root.toggleProcessList(); root.toggleProcessList();
} }
} }

View File

@@ -5,64 +5,13 @@ import qs.Services
Rectangle { Rectangle {
id: root id: root
// Dynamic screen detection for laptop vs desktop monitor property bool compactMode: false
readonly property bool isSmallScreen: { property int availableWidth: 400
// Walk up the parent chain to find the TopBar PanelWindow readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2
let current = root.parent readonly property int maxNormalWidth: 456
while (current && !current.screen) { readonly property int maxCompactWidth: 200
current = current.parent
}
if (!current || !current.screen) { width: compactMode ? Math.min(baseWidth, maxCompactWidth) : Math.min(baseWidth, maxNormalWidth)
return true // Default to small if we can't detect
}
const s = current.screen
// Multi-method detection for laptop/small screens:
// Method 1: Check screen name for laptop indicators
const screenName = (s.name || "").toLowerCase()
if (screenName.includes("edp") || screenName.includes("lvds")) {
return true
}
// Method 2: Check pixel density if available
try {
if (s.pixelDensity && s.pixelDensity > 1.5) {
return true
}
} catch (e) { /* ignore */ }
// Method 3: Check device pixel ratio if available
try {
if (s.devicePixelRatio && s.devicePixelRatio > 1.25) {
return true
}
} catch (e) { /* ignore */ }
// Method 4: Resolution-based fallback for smaller displays
if (s.width <= 1920 && s.height <= 1200) {
return true
}
// Method 5: Check for high-res laptop displays
if ((s.width === 2400 && s.height === 1600) ||
(s.width === 2560 && s.height === 1600) ||
(s.width === 2880 && s.height === 1800)) {
return true
}
return false // Default to large screen
}
// Dynamic sizing based on screen type
readonly property int maxWidth: isSmallScreen ? 288 : 456
readonly property int appNameMaxWidth: isSmallScreen ? 130 : 180
readonly property int separatorWidth: 15
readonly property int titleMaxWidth: maxWidth - appNameMaxWidth - separatorWidth - (Theme.spacingS * 2)
width: Math.min(contentRow.implicitWidth + Theme.spacingS * 2, maxWidth)
height: 30 height: 30
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: mouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) color: mouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
@@ -83,10 +32,9 @@ Rectangle {
font.weight: Font.Medium font.weight: Font.Medium
color: Theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// App name gets reasonable space - only truncate if absolutely necessary
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: 1 maximumLineCount: 1
width: Math.min(implicitWidth, root.appNameMaxWidth) width: Math.min(implicitWidth, compactMode ? 80 : 180)
} }
Text { Text {
@@ -105,17 +53,15 @@ Rectangle {
font.weight: Font.Medium font.weight: Font.Medium
color: Theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// Window title gets remaining space and shows ellipsis when truncated
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: 1 maximumLineCount: 1
width: Math.min(implicitWidth, root.titleMaxWidth) width: Math.min(implicitWidth, compactMode ? 60 : 250)
visible: !compactMode || text.length < 15
} }
} }
MouseArea { MouseArea {
// Non-interactive widget - just provides hover state for visual feedback
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
@@ -130,7 +76,6 @@ Rectangle {
} }
// Smooth width animation when the text changes
Behavior on width { Behavior on width {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration

View File

@@ -9,59 +9,10 @@ Rectangle {
readonly property MprisPlayer activePlayer: MprisController.activePlayer readonly property MprisPlayer activePlayer: MprisController.activePlayer
readonly property bool playerAvailable: activePlayer !== null readonly property bool playerAvailable: activePlayer !== null
property bool compactMode: false
// Screen detection for responsive design (same logic as FocusedApp) readonly property int baseContentWidth: mediaRow.implicitWidth + Theme.spacingS * 2
readonly property bool isSmallScreen: { readonly property int normalContentWidth: Math.min(280, baseContentWidth)
// Walk up the parent chain to find the TopBar PanelWindow readonly property int compactContentWidth: Math.min(120, baseContentWidth)
let current = root.parent
while (current && !current.screen) {
current = current.parent
}
if (!current || !current.screen) {
return true // Default to small if we can't detect
}
const s = current.screen
// Multi-method detection for laptop/small screens:
// Method 1: Check screen name for laptop indicators
const screenName = (s.name || "").toLowerCase()
if (screenName.includes("edp") || screenName.includes("lvds")) {
return true
}
// Method 2: Check pixel density if available
try {
if (s.pixelDensity && s.pixelDensity > 1.5) {
return true
}
} catch (e) { /* ignore */ }
// Method 3: Check device pixel ratio if available
try {
if (s.devicePixelRatio && s.devicePixelRatio > 1.25) {
return true
}
} catch (e) { /* ignore */ }
// Method 4: Resolution-based fallback for smaller displays
if (s.width <= 1920 && s.height <= 1200) {
return true
}
// Method 5: Check for high-res laptop displays
if ((s.width === 2400 && s.height === 1600) ||
(s.width === 2560 && s.height === 1600) ||
(s.width === 2880 && s.height === 1800)) {
return true
}
return false // Default to large screen
}
readonly property int contentWidth: Math.min(280, mediaRow.implicitWidth + Theme.spacingS * 2)
signal clicked() signal clicked()
@@ -76,7 +27,7 @@ Rectangle {
PropertyChanges { PropertyChanges {
target: root target: root
opacity: 1 opacity: 1
width: contentWidth width: compactMode ? compactContentWidth : normalContentWidth
} }
}, },
@@ -130,7 +81,6 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
// Media info section (clickable to open full player)
Row { Row {
id: mediaInfo id: mediaInfo
@@ -144,8 +94,8 @@ Rectangle {
id: mediaText id: mediaText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 140 width: compactMode ? 60 : 140
visible: !root.isSmallScreen // Hide title text on small screens visible: !compactMode
text: { text: {
if (!activePlayer || !activePlayer.trackTitle) if (!activePlayer || !activePlayer.trackTitle)
return ""; return "";
@@ -185,12 +135,10 @@ Rectangle {
} }
// Control buttons
Row { Row {
spacing: Theme.spacingXS spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// Previous button
Rectangle { Rectangle {
width: 20 width: 20
height: 20 height: 20
@@ -222,7 +170,6 @@ Rectangle {
} }
// Play/Pause button
Rectangle { Rectangle {
width: 24 width: 24
height: 24 height: 24
@@ -252,7 +199,6 @@ Rectangle {
} }
// Next button
Rectangle { Rectangle {
width: 20 width: 20
height: 20 height: 20

View File

@@ -16,11 +16,9 @@ Rectangle {
height: 30 height: 30
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: ramArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08) color: ramArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
Component.onCompleted: { Component.onCompleted: {
SysMonitorService.addRef(); SysMonitorService.addRef();
} }
Component.onDestruction: { Component.onDestruction: {
SysMonitorService.removeRef(); SysMonitorService.removeRef();
} }
@@ -35,6 +33,7 @@ Rectangle {
SysMonitorService.setSortBy("memory"); SysMonitorService.setSortBy("memory");
if (root.toggleProcessList) if (root.toggleProcessList)
root.toggleProcessList(); root.toggleProcessList();
} }
} }

View File

@@ -47,11 +47,14 @@ Rectangle {
Behavior on color { Behavior on color {
enabled: trayItemArea.containsMouse !== undefined enabled: trayItemArea.containsMouse !== undefined
ColorAnimation { ColorAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }
Image { Image {
@@ -66,23 +69,27 @@ Rectangle {
MouseArea { MouseArea {
id: trayItemArea id: trayItemArea
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => { onClicked: (mouse) => {
if (!trayItem) return; if (!trayItem)
return ;
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
if (!trayItem.onlyMenu) if (!trayItem.onlyMenu)
trayItem.activate(); trayItem.activate();
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
if (trayItem && trayItem.hasMenu) { if (trayItem && trayItem.hasMenu)
root.menuRequested(null, trayItem, mouse.x, mouse.y); root.menuRequested(null, trayItem, mouse.x, mouse.y);
}
} }
} }
} }
} }
} }

View File

@@ -110,6 +110,27 @@ PanelWindow {
} }
Item { Item {
id: topBarContent
readonly property int availableWidth: width
readonly property int leftSectionWidth: leftSection.width
readonly property int rightSectionWidth: rightSection.width
readonly property int clockWidth: clock.width
readonly property int mediaWidth: media.visible ? media.width : 0
readonly property int weatherWidth: weather.visible ? weather.width : 0
readonly property bool validLayout: availableWidth > 100 && leftSectionWidth > 0 && rightSectionWidth > 0
readonly property int clockLeftEdge: validLayout ? (availableWidth - clockWidth) / 2 : 0
readonly property int clockRightEdge: clockLeftEdge + clockWidth
readonly property int leftSectionRightEdge: leftSectionWidth
readonly property int mediaLeftEdge: clockLeftEdge - mediaWidth - Theme.spacingS
readonly property int rightSectionLeftEdge: availableWidth - rightSectionWidth
readonly property int leftToClockGap: validLayout ? Math.max(0, clockLeftEdge - leftSectionRightEdge) : 1000
readonly property int leftToMediaGap: (mediaWidth > 0 && validLayout) ? Math.max(0, mediaLeftEdge - leftSectionRightEdge) : leftToClockGap
readonly property int mediaToClockGap: mediaWidth > 0 ? Theme.spacingS : 0
readonly property int clockToRightGap: validLayout ? Math.max(0, rightSectionLeftEdge - clockRightEdge) : 1000
readonly property bool spacingTight: validLayout && (leftToMediaGap < 100 || clockToRightGap < 100)
readonly property bool overlapping: validLayout && (leftToMediaGap < 50 || clockToRightGap < 50)
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingM anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM anchors.rightMargin: Theme.spacingM
@@ -131,6 +152,7 @@ PanelWindow {
onClicked: { onClicked: {
if (appDrawerPopout) if (appDrawerPopout)
appDrawerPopout.toggle(); appDrawerPopout.toggle();
} }
} }
@@ -140,8 +162,12 @@ PanelWindow {
} }
FocusedApp { FocusedApp {
id: focusedApp
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showFocusedWindow visible: Prefs.showFocusedWindow
compactMode: topBarContent.spacingTight
availableWidth: topBarContent.leftToMediaGap
} }
} }
@@ -150,16 +176,20 @@ PanelWindow {
id: clock id: clock
anchors.centerIn: parent anchors.centerIn: parent
compactMode: topBarContent.overlapping
onClockClicked: { onClockClicked: {
centcomPopout.calendarVisible = !centcomPopout.calendarVisible; centcomPopout.calendarVisible = !centcomPopout.calendarVisible;
} }
} }
Media { Media {
id: media
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: clock.left anchors.right: clock.left
anchors.rightMargin: Theme.spacingS anchors.rightMargin: Theme.spacingS
visible: Prefs.showMusic && MprisController.activePlayer visible: Prefs.showMusic && MprisController.activePlayer
compactMode: topBarContent.spacingTight || topBarContent.overlapping
onClicked: { onClicked: {
centcomPopout.calendarVisible = !centcomPopout.calendarVisible; centcomPopout.calendarVisible = !centcomPopout.calendarVisible;
} }
@@ -237,21 +267,31 @@ PanelWindow {
Loader { Loader {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
active: Prefs.showSystemResources active: Prefs.showSystemResources
sourceComponent: Component { sourceComponent: Component {
CpuMonitor { CpuMonitor {
toggleProcessList: () => processListPopout.toggle() toggleProcessList: () => {
return processListPopout.toggle();
}
} }
} }
} }
Loader { Loader {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
active: Prefs.showSystemResources active: Prefs.showSystemResources
sourceComponent: Component { sourceComponent: Component {
RamMonitor { RamMonitor {
toggleProcessList: () => processListPopout.toggle() toggleProcessList: () => {
return processListPopout.toggle();
}
} }
} }
} }
NotificationCenterButton { NotificationCenterButton {