From aa025e4face2cb3846620d316cc022c3a6cedd87 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 11 Aug 2025 22:05:19 +0700 Subject: [PATCH] bar tooltips: refractor more --- .../ii/modules/bar/BatteryIndicator.qml | 8 +- .../ii/modules/bar/BatteryPopup.qml | 210 +++++++++--------- .../quickshell/ii/modules/bar/ClockWidget.qml | 106 +-------- .../ii/modules/bar/ClockWidgetTooltip.qml | 109 +++++++++ .../ii/modules/bar/ResourcePopup.qml | 65 ++++++ .../ii/modules/bar/weather/WeatherBar.qml | 10 +- .../ii/modules/bar/weather/WeatherPopup.qml | 145 ++++++------ 7 files changed, 363 insertions(+), 290 deletions(-) create mode 100644 .config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml create mode 100644 .config/quickshell/ii/modules/bar/ResourcePopup.qml diff --git a/.config/quickshell/ii/modules/bar/BatteryIndicator.qml b/.config/quickshell/ii/modules/bar/BatteryIndicator.qml index 91d811a4..e437f1d4 100644 --- a/.config/quickshell/ii/modules/bar/BatteryIndicator.qml +++ b/.config/quickshell/ii/modules/bar/BatteryIndicator.qml @@ -94,12 +94,8 @@ MouseArea { } } - StyledPopup { + BatteryPopup { + id: batteryPopup hoverTarget: root - - BatteryPopup { - id: batteryPopup - anchors.centerIn: parent - } } } diff --git a/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/.config/quickshell/ii/modules/bar/BatteryPopup.qml index 9b9edc5a..c697bfbd 100644 --- a/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -5,124 +5,128 @@ import qs import QtQuick import QtQuick.Layouts -ColumnLayout { - id: columnLayout - anchors.centerIn: parent - spacing: 4 +StyledPopup { + id: root + + ColumnLayout { + id: columnLayout + anchors.centerIn: parent + spacing: 4 - // Header - RowLayout { - id: header - spacing: 5 + // Header + RowLayout { + id: header + spacing: 5 - MaterialSymbol { - fill: 0 - font.weight: Font.Medium - text: "battery_android_full" - iconSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnSurfaceVariant - } - - StyledText { - text: "Battery" - font { - weight: Font.Medium - pixelSize: Appearance.font.pixelSize.normal + MaterialSymbol { + fill: 0 + font.weight: Font.Medium + text: "battery_android_full" + iconSize: Appearance.font.pixelSize.large + color: Appearance.colors.colOnSurfaceVariant } - color: Appearance.colors.colOnSurfaceVariant - } - } - // This row is hidden when the battery is full. - RowLayout { - spacing: 5 - Layout.fillWidth: true - property bool rowVisible: { - let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty; - let power = Battery.energyRate; - return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01); - } - visible: rowVisible - opacity: rowVisible ? 1 : 0 - Behavior on opacity { - NumberAnimation { - duration: 500 + StyledText { + text: "Battery" + font { + weight: Font.Medium + pixelSize: Appearance.font.pixelSize.normal + } + color: Appearance.colors.colOnSurfaceVariant } } - MaterialSymbol { - text: "schedule" - color: Appearance.colors.colOnSurfaceVariant - iconSize: Appearance.font.pixelSize.large - } - StyledText { - text: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:") - color: Appearance.colors.colOnSurfaceVariant - } - StyledText { + // This row is hidden when the battery is full. + RowLayout { + spacing: 5 Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: { - function formatTime(seconds) { - var h = Math.floor(seconds / 3600); - var m = Math.floor((seconds % 3600) / 60); - if (h > 0) - return `${h}h, ${m}m`; + property bool rowVisible: { + let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty; + let power = Battery.energyRate; + return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01); + } + visible: rowVisible + opacity: rowVisible ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: 500 + } + } + + MaterialSymbol { + text: "schedule" + color: Appearance.colors.colOnSurfaceVariant + iconSize: Appearance.font.pixelSize.large + } + StyledText { + text: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:") + color: Appearance.colors.colOnSurfaceVariant + } + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + color: Appearance.colors.colOnSurfaceVariant + text: { + function formatTime(seconds) { + var h = Math.floor(seconds / 3600); + var m = Math.floor((seconds % 3600) / 60); + if (h > 0) + return `${h}h, ${m}m`; + else + return `${m}m`; + } + if (Battery.isCharging) + return formatTime(Battery.timeToFull); else - return `${m}m`; - } - if (Battery.isCharging) - return formatTime(Battery.timeToFull); - else - return formatTime(Battery.timeToEmpty); - } - } - } - - RowLayout { - spacing: 5 - Layout.fillWidth: true - - property bool rowVisible: !(Battery.chargeState != 4 && Battery.energyRate == 0) - visible: rowVisible - opacity: rowVisible ? 1 : 0 - Behavior on opacity { - NumberAnimation { - duration: 500 - } - } - - MaterialSymbol { - text: "bolt" - color: Appearance.colors.colOnSurfaceVariant - iconSize: Appearance.font.pixelSize.large - } - - StyledText { - text: { - if (Battery.chargeState == 4) { - return Translation.tr("Fully charged"); - } else if (Battery.chargeState == 1) { - return Translation.tr("Charging:"); - } else { - return Translation.tr("Discharging:"); + return formatTime(Battery.timeToEmpty); } } - color: Appearance.colors.colOnSurfaceVariant } - StyledText { + RowLayout { + spacing: 5 Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: { - if (Battery.chargeState == 4) { - return ""; - } else { - return `${Battery.energyRate.toFixed(2)}W`; + + property bool rowVisible: !(Battery.chargeState != 4 && Battery.energyRate == 0) + visible: rowVisible + opacity: rowVisible ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: 500 + } + } + + MaterialSymbol { + text: "bolt" + color: Appearance.colors.colOnSurfaceVariant + iconSize: Appearance.font.pixelSize.large + } + + StyledText { + text: { + if (Battery.chargeState == 4) { + return Translation.tr("Fully charged"); + } else if (Battery.chargeState == 1) { + return Translation.tr("Charging:"); + } else { + return Translation.tr("Discharging:"); + } + } + color: Appearance.colors.colOnSurfaceVariant + } + + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + color: Appearance.colors.colOnSurfaceVariant + text: { + if (Battery.chargeState == 4) { + return ""; + } else { + return `${Battery.energyRate.toFixed(2)}W`; + } } } } } -} \ No newline at end of file +} diff --git a/.config/quickshell/ii/modules/bar/ClockWidget.qml b/.config/quickshell/ii/modules/bar/ClockWidget.qml index 5c60141a..6f335de2 100644 --- a/.config/quickshell/ii/modules/bar/ClockWidget.qml +++ b/.config/quickshell/ii/modules/bar/ClockWidget.qml @@ -13,34 +13,6 @@ Item { implicitWidth: rowLayout.implicitWidth implicitHeight: 32 - // Helper function to get upcoming todos - function getUpcomingTodos() { - const unfinishedTodos = Todo.list.filter(function (item) { - return !item.done; - }); - if (unfinishedTodos.length === 0) { - return Translation.tr("No pending tasks"); - } - - // Limit to first 5 todos to keep popup manageable - const limitedTodos = unfinishedTodos.slice(0, 5); - let todoText = limitedTodos.map(function (item, index) { - return `${index + 1}. ${item.content}`; - }).join('\n'); - - if (unfinishedTodos.length > 5) { - todoText += `\n${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`; - } - - return todoText; - } - - // 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() - RowLayout { id: rowLayout anchors.centerIn: parent @@ -72,83 +44,9 @@ Item { anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.NoButton - } - StyledPopup { - hoverTarget: mouseArea - - ColumnLayout { - id: columnLayout - anchors.centerIn: parent - spacing: 4 - - // Date + Time row - RowLayout { - spacing: 5 - - MaterialSymbol { - fill: 0 - font.weight: Font.Medium - text: "calendar_month" - iconSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnSurfaceVariant - } - StyledText { - horizontalAlignment: Text.AlignLeft - color: Appearance.colors.colOnSurfaceVariant - text: `${root.formattedDate} • ${root.formattedTime}` - font.weight: Font.Medium - } - } - - // Uptime row - RowLayout { - spacing: 5 - Layout.fillWidth: true - MaterialSymbol { - text: "timelapse" - color: Appearance.colors.colOnSurfaceVariant - font.pixelSize: Appearance.font.pixelSize.large - } - StyledText { - text: Translation.tr("System uptime:") - color: Appearance.colors.colOnSurfaceVariant - } - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: root.formattedUptime - } - } - - // Tasks - ColumnLayout { - spacing: 0 - Layout.fillWidth: true - - RowLayout { - spacing: 4 - Layout.fillWidth: true - MaterialSymbol { - text: "checklist" - color: Appearance.colors.colOnSurfaceVariant - font.pixelSize: Appearance.font.pixelSize.large - } - StyledText { - text: Translation.tr("To Do:") - color: Appearance.colors.colOnSurfaceVariant - } - } - - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignLeft - wrapMode: Text.Wrap - color: Appearance.colors.colOnSurfaceVariant - text: root.todosSection - } - } + ClockWidgetTooltip { + hoverTarget: mouseArea } } } diff --git a/.config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml b/.config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml new file mode 100644 index 00000000..ea11336a --- /dev/null +++ b/.config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml @@ -0,0 +1,109 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Layouts + +StyledPopup { + id: root + 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() + + function getUpcomingTodos() { + const unfinishedTodos = Todo.list.filter(function (item) { + return !item.done; + }); + if (unfinishedTodos.length === 0) { + return Translation.tr("No pending tasks"); + } + + // Limit to first 5 todos to keep popup manageable + const limitedTodos = unfinishedTodos.slice(0, 5); + let todoText = limitedTodos.map(function (item, index) { + return `${index + 1}. ${item.content}`; + }).join('\n'); + + if (unfinishedTodos.length > 5) { + todoText += `\n${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`; + } + + return todoText; + } + + ColumnLayout { + id: columnLayout + anchors.centerIn: parent + spacing: 4 + + // Date + Time row + RowLayout { + spacing: 5 + + MaterialSymbol { + fill: 0 + font.weight: Font.Medium + text: "calendar_month" + iconSize: Appearance.font.pixelSize.large + color: Appearance.colors.colOnSurfaceVariant + } + StyledText { + horizontalAlignment: Text.AlignLeft + color: Appearance.colors.colOnSurfaceVariant + text: `${root.formattedDate} • ${root.formattedTime}` + font.weight: Font.Medium + } + } + + // Uptime row + RowLayout { + spacing: 5 + Layout.fillWidth: true + MaterialSymbol { + text: "timelapse" + color: Appearance.colors.colOnSurfaceVariant + font.pixelSize: Appearance.font.pixelSize.large + } + StyledText { + text: Translation.tr("System uptime:") + color: Appearance.colors.colOnSurfaceVariant + } + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + color: Appearance.colors.colOnSurfaceVariant + text: root.formattedUptime + } + } + + // Tasks + ColumnLayout { + spacing: 0 + Layout.fillWidth: true + + RowLayout { + spacing: 4 + Layout.fillWidth: true + MaterialSymbol { + text: "checklist" + color: Appearance.colors.colOnSurfaceVariant + font.pixelSize: Appearance.font.pixelSize.large + } + StyledText { + text: Translation.tr("To Do:") + color: Appearance.colors.colOnSurfaceVariant + } + } + + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + wrapMode: Text.Wrap + color: Appearance.colors.colOnSurfaceVariant + text: root.todosSection + } + } + } +} diff --git a/.config/quickshell/ii/modules/bar/ResourcePopup.qml b/.config/quickshell/ii/modules/bar/ResourcePopup.qml new file mode 100644 index 00000000..71fd0244 --- /dev/null +++ b/.config/quickshell/ii/modules/bar/ResourcePopup.qml @@ -0,0 +1,65 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Layouts + +StyledPopup { + hoverTarget: mouseArea + + ColumnLayout { + id: columnLayout + anchors.centerIn: parent + spacing: 4 + + // Header + RowLayout { + id: header + spacing: 5 + + MaterialSymbol { + fill: 0 + font.weight: Font.Medium + text: root.tooltipHeaderIcon + iconSize: Appearance.font.pixelSize.large + color: Appearance.colors.colOnSurfaceVariant + } + + StyledText { + text: root.tooltipHeaderText + font { + weight: Font.Medium + pixelSize: Appearance.font.pixelSize.normal + } + color: Appearance.colors.colOnSurfaceVariant + } + } + + // Info rows + Repeater { + model: root.tooltipData + delegate: RowLayout { + spacing: 5 + Layout.fillWidth: true + + MaterialSymbol { + text: modelData.icon + color: Appearance.colors.colOnSurfaceVariant + iconSize: Appearance.font.pixelSize.large + } + StyledText { + text: modelData.label + color: Appearance.colors.colOnSurfaceVariant + } + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + visible: modelData.value !== "" + color: Appearance.colors.colOnSurfaceVariant + text: modelData.value + } + } + } + } +} \ No newline at end of file diff --git a/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml b/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml index ed49b4b5..c49a1b7d 100644 --- a/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml +++ b/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml @@ -21,7 +21,7 @@ MouseArea { MaterialSymbol { fill: 0 - text: WeatherIcons.codeToName[Weather.data.wCode] + text: WeatherIcons.codeToName[Weather.data.wCode] ?? "cloud" iconSize: Appearance.font.pixelSize.large color: Appearance.colors.colOnLayer1 Layout.alignment: Qt.AlignVCenter @@ -36,12 +36,8 @@ MouseArea { } } - StyledPopup { + WeatherPopup { + id: weatherPopup hoverTarget: root - - WeatherPopup { - id: weatherPopup - anchors.centerIn: parent - } } } diff --git a/.config/quickshell/ii/modules/bar/weather/WeatherPopup.qml b/.config/quickshell/ii/modules/bar/weather/WeatherPopup.qml index 6297ea8c..aa61359d 100644 --- a/.config/quickshell/ii/modules/bar/weather/WeatherPopup.qml +++ b/.config/quickshell/ii/modules/bar/weather/WeatherPopup.qml @@ -6,83 +6,88 @@ import qs.modules.common.widgets import QtQuick import QtQuick.Layouts -ColumnLayout { - id: columnLayout - spacing: 5 - implicitWidth: Math.max(header.implicitWidth, gridLayout.implicitWidth) - implicitHeight: gridLayout.implicitHeight +StyledPopup { + id: root - // Header - RowLayout { - id: header + ColumnLayout { + id: columnLayout + anchors.centerIn: parent + implicitWidth: Math.max(header.implicitWidth, gridLayout.implicitWidth) + implicitHeight: gridLayout.implicitHeight spacing: 5 - Layout.alignment: Qt.AlignHCenter - MaterialSymbol { - fill: 0 - font.weight: Font.Medium - text: "location_on" - iconSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnSurfaceVariant - } + // Header + RowLayout { + id: header + spacing: 5 + Layout.alignment: Qt.AlignHCenter - StyledText { - text: Weather.data.city - font { - weight: Font.Medium - pixelSize: Appearance.font.pixelSize.normal + MaterialSymbol { + fill: 0 + font.weight: Font.Medium + text: "location_on" + iconSize: Appearance.font.pixelSize.large + color: Appearance.colors.colOnSurfaceVariant } - color: Appearance.colors.colOnSurfaceVariant - } - } - // Metrics grid - GridLayout { - id: gridLayout - columns: 2 - rowSpacing: 5 - columnSpacing: 5 - uniformCellWidths: true + StyledText { + text: Weather.data.city + font { + weight: Font.Medium + pixelSize: Appearance.font.pixelSize.normal + } + color: Appearance.colors.colOnSurfaceVariant + } + } - WeatherCard { - title: Translation.tr("UV Index") - symbol: "wb_sunny" - value: Weather.data.uv - } - WeatherCard { - title: Translation.tr("Wind") - symbol: "air" - value: `(${Weather.data.windDir}) ${Weather.data.wind}` - } - WeatherCard { - title: Translation.tr("Precipitation") - symbol: "rainy_light" - value: Weather.data.precip - } - WeatherCard { - title: Translation.tr("Humidity") - symbol: "humidity_low" - value: Weather.data.humidity - } - WeatherCard { - title: Translation.tr("Visibility") - symbol: "visibility" - value: Weather.data.visib - } - WeatherCard { - title: Translation.tr("Pressure") - symbol: "readiness_score" - value: Weather.data.press - } - WeatherCard { - title: Translation.tr("Sunrise") - symbol: "wb_twilight" - value: Weather.data.sunrise - } - WeatherCard { - title: Translation.tr("Sunset") - symbol: "bedtime" - value: Weather.data.sunset + // Metrics grid + GridLayout { + id: gridLayout + columns: 2 + rowSpacing: 5 + columnSpacing: 5 + uniformCellWidths: true + + WeatherCard { + title: Translation.tr("UV Index") + symbol: "wb_sunny" + value: Weather.data.uv + } + WeatherCard { + title: Translation.tr("Wind") + symbol: "air" + value: `(${Weather.data.windDir}) ${Weather.data.wind}` + } + WeatherCard { + title: Translation.tr("Precipitation") + symbol: "rainy_light" + value: Weather.data.precip + } + WeatherCard { + title: Translation.tr("Humidity") + symbol: "humidity_low" + value: Weather.data.humidity + } + WeatherCard { + title: Translation.tr("Visibility") + symbol: "visibility" + value: Weather.data.visib + } + WeatherCard { + title: Translation.tr("Pressure") + symbol: "readiness_score" + value: Weather.data.press + } + WeatherCard { + title: Translation.tr("Sunrise") + symbol: "wb_twilight" + value: Weather.data.sunrise + } + WeatherCard { + title: Translation.tr("Sunset") + symbol: "bedtime" + value: Weather.data.sunset + } } } } \ No newline at end of file