mirror of
https://github.com/danbulant/dots-hyprland
synced 2026-05-24 12:22:09 +00:00
feat(bar): unify popup handling and improve layouts
- Unified popup handling in ClockWidget, Resource, BatteryPopup, and WeatherBar using PanelWindow + LazyLoader for consistent positioning and compositor animations. - Replaced plain text with ColumnLayout and RowLayout where possible, adding MaterialSymbol icons for improved visual consistency with the overall desktop style. - Added Translation.tr() for bilingual (Chinese/English) support to avoid hardcoded strings. - Based on improvements from PR #1771 (mine) and PR #1773 (by @finjener), merged and refined into a more polished and practical solution.
This commit is contained in:
parent
1dc46fa104
commit
061bb2abeb
7 changed files with 224 additions and 72 deletions
|
|
@ -95,11 +95,11 @@ MouseArea {
|
|||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
LazyLoader {
|
||||
id: popupLoader
|
||||
active: root.containsMouse
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
component: PanelWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
color: "transparent"
|
||||
|
|
@ -112,7 +112,10 @@ MouseArea {
|
|||
implicitHeight: batteryPopup.implicitHeight
|
||||
|
||||
margins {
|
||||
left: root.mapToGlobal(Qt.point(0, 0)).x - batteryPopup.implicitWidth / 3
|
||||
left: root.mapToGlobal(Qt.point(
|
||||
(root.width - batteryPopup.implicitWidth) / 2,
|
||||
0
|
||||
)).x
|
||||
top: root.mapToGlobal(Qt.point(0, root.height)).y - 30
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ Rectangle {
|
|||
var h = Math.floor(seconds / 3600);
|
||||
var m = Math.floor((seconds % 3600) / 60);
|
||||
if (h > 0)
|
||||
return `${h}h ${m}m`;
|
||||
return `${h}h, ${m}m`;
|
||||
else
|
||||
return `${m}m`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Item {
|
|||
function getUpcomingTodos() {
|
||||
const unfinishedTodos = Todo.list.filter(function(item) { return !item.done; })
|
||||
if (unfinishedTodos.length === 0) {
|
||||
return "No pending tasks"
|
||||
return Translation.tr("No pending tasks")
|
||||
}
|
||||
|
||||
// Limit to first 5 todos to keep popup manageable
|
||||
|
|
@ -27,21 +27,17 @@ Item {
|
|||
}).join('\n')
|
||||
|
||||
if (unfinishedTodos.length > 5) {
|
||||
todoText += `\n... and ${unfinishedTodos.length - 5} more`
|
||||
todoText += `\n${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`
|
||||
}
|
||||
|
||||
return todoText
|
||||
}
|
||||
|
||||
// Generate popup content with date and upcoming todos
|
||||
property string dateDetails: {
|
||||
const todosSection = getUpcomingTodos()
|
||||
return `${Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy")} • ${DateTime.time}
|
||||
Uptime: ${DateTime.uptime}
|
||||
|
||||
📋 Upcoming Tasks:
|
||||
${todosSection}`
|
||||
}
|
||||
// Popup Data
|
||||
property string formattedDate: Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy")
|
||||
property string formattedTime: DateTime.time
|
||||
property string formattedUptime: DateTime.uptime
|
||||
property string todosSection: getUpcomingTodos()
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
|
@ -54,37 +50,95 @@ ${todosSection}`
|
|||
id: popupLoader
|
||||
active: mouseArea.containsMouse
|
||||
|
||||
component: PopupWindow {
|
||||
component: PanelWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
implicitWidth: datePopup.implicitWidth
|
||||
implicitHeight: datePopup.implicitHeight
|
||||
anchor.item: root
|
||||
anchor.edges: Edges.Top
|
||||
anchor.rect.x: (root.implicitWidth - popupWindow.implicitWidth) / 2
|
||||
anchor.rect.y: Config.options.bar.bottom ?
|
||||
(-datePopup.implicitHeight - 15) :
|
||||
(root.implicitHeight + 15)
|
||||
color: "transparent"
|
||||
exclusiveZone: 0
|
||||
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
|
||||
margins {
|
||||
left: root.mapToGlobal(Qt.point(
|
||||
(root.width - datePopup.implicitWidth) / 2,
|
||||
0
|
||||
)).x
|
||||
top: root.mapToGlobal(Qt.point(0, root.height)).y - 30
|
||||
}
|
||||
|
||||
mask: Region {
|
||||
item: datePopup
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: datePopup
|
||||
readonly property real margin: 12
|
||||
implicitWidth: popupText.implicitWidth + margin * 2
|
||||
implicitHeight: popupText.implicitHeight + margin * 2
|
||||
implicitWidth: columnLayout.implicitWidth + margin * 2
|
||||
implicitHeight: columnLayout.implicitHeight + margin * 2
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.small
|
||||
border.width: 1
|
||||
border.color: Appearance.colors.colLayer0Border
|
||||
|
||||
StyledText {
|
||||
id: popupText
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer0
|
||||
text: dateDetails
|
||||
spacing: 8
|
||||
|
||||
// Date + Time row
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: `${root.formattedDate} • ${root.formattedTime}`
|
||||
}
|
||||
}
|
||||
|
||||
// Uptime row
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
MaterialSymbol { text: "timelapse"; color: Appearance.m3colors.m3onSecondaryContainer }
|
||||
StyledText { text: Translation.tr("Uptime:"); color: Appearance.colors.colOnLayer1 }
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: root.formattedUptime
|
||||
}
|
||||
}
|
||||
|
||||
// Upcoming tasks row
|
||||
ColumnLayout {
|
||||
spacing: 2
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
MaterialSymbol { text: "checklist"; color: Appearance.m3colors.m3onSecondaryContainer }
|
||||
StyledText { text: Translation.tr("Upcoming Tasks:"); color: Appearance.colors.colOnLayer1 }
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
topPadding: 5
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: root.todosSection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import QtQuick.Layouts
|
|||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property string iconName
|
||||
required property double percentage
|
||||
property bool shown: true
|
||||
|
|
@ -21,28 +22,41 @@ Item {
|
|||
}
|
||||
|
||||
// Generate tooltip content based on resource type
|
||||
property string tooltipContent: {
|
||||
property var tooltipData: {
|
||||
switch(iconName) {
|
||||
case "memory":
|
||||
return `Memory Usage
|
||||
Used: ${formatKB(ResourceUsage.memoryUsed)}
|
||||
Free: ${formatKB(ResourceUsage.memoryFree)}
|
||||
Total: ${formatKB(ResourceUsage.memoryTotal)}
|
||||
Usage: ${Math.round(ResourceUsage.memoryUsedPercentage * 100)}%`
|
||||
return [
|
||||
{ icon: "memory", label: Translation.tr("Memory Usage"), value: "" },
|
||||
{ icon: "storage", label: Translation.tr("Used:"), value: formatKB(ResourceUsage.memoryUsed) },
|
||||
{ icon: "check_circle", label: Translation.tr("Free:"), value: formatKB(ResourceUsage.memoryFree) },
|
||||
{ icon: "dns", label: Translation.tr("Total:"), value: formatKB(ResourceUsage.memoryTotal) },
|
||||
{ icon: "percent", label: Translation.tr("Usage:"), value: `${Math.round(ResourceUsage.memoryUsedPercentage * 100)}%` }
|
||||
]
|
||||
case "swap_horiz":
|
||||
return ResourceUsage.swapTotal > 0 ?
|
||||
`Swap Usage
|
||||
Used: ${formatKB(ResourceUsage.swapUsed)}
|
||||
Free: ${formatKB(ResourceUsage.swapFree)}
|
||||
Total: ${formatKB(ResourceUsage.swapTotal)}
|
||||
Usage: ${Math.round(ResourceUsage.swapUsedPercentage * 100)}%` :
|
||||
"Swap: Not configured"
|
||||
return ResourceUsage.swapTotal > 0 ?
|
||||
[
|
||||
{ icon: "swap_horiz", label: Translation.tr("Swap Usage"), value: "" },
|
||||
{ icon: "storage", label: Translation.tr("Used:"), value: formatKB(ResourceUsage.swapUsed) },
|
||||
{ icon: "check_circle", label: Translation.tr("Free:"), value: formatKB(ResourceUsage.swapFree) },
|
||||
{ icon: "dns", label: Translation.tr("Total:"), value: formatKB(ResourceUsage.swapTotal) },
|
||||
{ icon: "percent", label: Translation.tr("Usage:"), value: `${Math.round(ResourceUsage.swapUsedPercentage * 100)}%` }
|
||||
] :
|
||||
[
|
||||
{ icon: "swap_horiz", label: Translation.tr("Swap:"), value: Translation.tr("Not configured") }
|
||||
]
|
||||
case "settings_slow_motion":
|
||||
return `CPU Usage
|
||||
Current: ${Math.round(ResourceUsage.cpuUsage * 100)}%
|
||||
Load: ${ResourceUsage.cpuUsage > 0.8 ? "High" : ResourceUsage.cpuUsage > 0.5 ? "Medium" : "Low"}`
|
||||
return [
|
||||
{ icon: "settings_slow_motion", label: Translation.tr("CPU Usage"), value: "" },
|
||||
{ icon: "bolt", label: Translation.tr("Current:"), value: `${Math.round(ResourceUsage.cpuUsage * 100)}%` },
|
||||
{ icon: "speed", label: Translation.tr("Load:"), value: ResourceUsage.cpuUsage > 0.8 ?
|
||||
Translation.tr("High") :
|
||||
ResourceUsage.cpuUsage > 0.5 ? Translation.tr("Medium") : Translation.tr("Low")
|
||||
}
|
||||
]
|
||||
default:
|
||||
return "System Resource"
|
||||
return [
|
||||
{ icon: "info", label: Translation.tr("System Resource"), value: "" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,35 +71,66 @@ Load: ${ResourceUsage.cpuUsage > 0.8 ? "High" : ResourceUsage.cpuUsage > 0.5 ? "
|
|||
id: popupLoader
|
||||
active: mouseArea.containsMouse
|
||||
|
||||
component: PopupWindow {
|
||||
component: PanelWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
|
||||
color: "transparent"
|
||||
exclusiveZone: 0
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
|
||||
implicitWidth: resourcePopup.implicitWidth
|
||||
implicitHeight: resourcePopup.implicitHeight
|
||||
anchor.item: root
|
||||
anchor.edges: Edges.Top
|
||||
anchor.rect.x: (root.implicitWidth - popupWindow.implicitWidth) / 2
|
||||
anchor.rect.y: Config.options.bar.bottom ?
|
||||
(-resourcePopup.implicitHeight - 15) :
|
||||
(root.implicitHeight + 15)
|
||||
color: "transparent"
|
||||
|
||||
margins {
|
||||
left: root.mapToGlobal(Qt.point(
|
||||
(root.width - resourcePopup.implicitWidth) / 2,
|
||||
0
|
||||
)).x
|
||||
top: root.mapToGlobal(Qt.point(0, root.height)).y - 30
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: resourcePopup
|
||||
readonly property real margin: 10
|
||||
implicitWidth: popupText.implicitWidth + margin * 2
|
||||
implicitHeight: popupText.implicitHeight + margin * 2
|
||||
implicitWidth: columnLayout.implicitWidth + margin * 2
|
||||
implicitHeight: columnLayout.implicitHeight + margin * 2
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.small
|
||||
border.width: 1
|
||||
border.color: Appearance.colors.colLayer0Border
|
||||
|
||||
StyledText {
|
||||
id: popupText
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer0
|
||||
text: tooltipContent
|
||||
spacing: 6
|
||||
|
||||
Repeater {
|
||||
model: root.tooltipData
|
||||
delegate: RowLayout {
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
|
||||
MaterialSymbol {
|
||||
text: modelData.icon
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
}
|
||||
StyledText {
|
||||
text: modelData.label
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
visible: modelData.value !== ""
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: modelData.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,18 +40,30 @@ MouseArea {
|
|||
id: popupLoader
|
||||
active: root.containsMouse
|
||||
|
||||
component: PopupWindow {
|
||||
component: PanelWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
implicitWidth: weatherPopup.implicitWidth
|
||||
implicitHeight: weatherPopup.implicitHeight
|
||||
anchor.item: root
|
||||
anchor.edges: Edges.Top
|
||||
anchor.rect.x: (root.implicitWidth - popupWindow.implicitWidth) / 2
|
||||
anchor.rect.y: Config.options.bar.bottom ?
|
||||
(-weatherPopup.implicitHeight - 15) :
|
||||
(root.implicitHeight + 15 )
|
||||
|
||||
color: "transparent"
|
||||
exclusiveZone: 0
|
||||
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
|
||||
margins {
|
||||
left: root.mapToGlobal(Qt.point(
|
||||
(root.width - weatherPopup.implicitWidth) / 2,
|
||||
0
|
||||
)).x
|
||||
top: root.mapToGlobal(Qt.point(0, root.height)).y - 25
|
||||
}
|
||||
|
||||
mask: Region {
|
||||
item: weatherPopup
|
||||
}
|
||||
|
||||
WeatherPopup {
|
||||
id: weatherPopup
|
||||
}
|
||||
|
|
|
|||
|
|
@ -316,5 +316,24 @@
|
|||
"Time to empty:": "Time to empty:",
|
||||
"Fully charged": "Fully charged",
|
||||
"Charging:": "Charging:",
|
||||
"Discharging:": "Discharging:"
|
||||
"Discharging:": "Discharging:",
|
||||
"Uptime:": "Uptime:",
|
||||
"Upcoming Tasks:": "Upcoming Tasks:",
|
||||
"No pending tasks": "No pending tasks",
|
||||
"... and %1 more": "... and %1 more",
|
||||
"Memory Usage": "Memory Usage",
|
||||
"Used:": "Used:",
|
||||
"Free:": "Free:",
|
||||
"Total:": "Total:",
|
||||
"Usage:": "Usage:",
|
||||
"Swap Usage": "Swap Usage",
|
||||
"Swap:": "Swap:",
|
||||
"Not configured": "Not configured",
|
||||
"CPU Usage": "CPU Usage",
|
||||
"Current:": "Current:",
|
||||
"Load:": "Load:",
|
||||
"High": "High",
|
||||
"Medium": "Medium",
|
||||
"Low": "Low",
|
||||
"System Resource": "System Resource"
|
||||
}
|
||||
|
|
@ -316,5 +316,24 @@
|
|||
"Time to empty:": "距离耗尽:",
|
||||
"Fully charged": "已充满电",
|
||||
"Charging:": "充电功率:",
|
||||
"Discharging:": "放电功率:"
|
||||
"Discharging:": "放电功率:",
|
||||
"Uptime:": "运行时间:",
|
||||
"Upcoming Tasks:": "待办任务:",
|
||||
"No pending tasks": "没有待办任务",
|
||||
"... and %1 more": "... 还有 %1 个",
|
||||
"Memory Usage": "内存使用情况",
|
||||
"Used:": "已用:",
|
||||
"Free:": "可用:",
|
||||
"Total:": "总计:",
|
||||
"Usage:": "占比:",
|
||||
"Swap Usage": "交换区使用情况",
|
||||
"Swap:": "交换区:",
|
||||
"Not configured": "未配置",
|
||||
"CPU Usage": "CPU 使用情况",
|
||||
"Current:": "当前占比:",
|
||||
"Load:": "负载:",
|
||||
"High": "高",
|
||||
"Medium": "中",
|
||||
"Low": "低",
|
||||
"System Resource": "系统资源"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue