bar tooltips: refractor tooltip bg to StyledPopup

This commit is contained in:
end-4
2025-08-11 20:33:01 +07:00
parent 291b997972
commit 6a8a9172b1
7 changed files with 284 additions and 315 deletions
@@ -97,7 +97,8 @@ MouseArea {
StyledPopup {
hoverTarget: root
contentComponent: BatteryPopup {
BatteryPopup {
id: batteryPopup
anchors.centerIn: parent
}
+102 -112
View File
@@ -5,134 +5,124 @@ import qs
import QtQuick
import QtQuick.Layouts
Rectangle {
id: root
readonly property real margin: 10
implicitWidth: columnLayout.implicitWidth + margin * 2
implicitHeight: columnLayout.implicitHeight + margin * 2
color: Appearance.colors.colSurfaceContainer
radius: Appearance.rounding.small
clip: true
ColumnLayout {
id: columnLayout
anchors.centerIn: parent
spacing: 4
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
}
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
}
color: Appearance.colors.colOnSurfaceVariant
}
}
StyledText {
text: "Battery"
font {
weight: Font.Medium
pixelSize: Appearance.font.pixelSize.normal
}
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
}
}
// This row is hidden when the battery is full.
RowLayout {
spacing: 5
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
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);
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 formatTime(Battery.timeToEmpty);
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
}
}
RowLayout {
spacing: 5
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
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`;
}
horizontalAlignment: Text.AlignRight
color: Appearance.colors.colOnSurfaceVariant
text: {
if (Battery.chargeState == 4) {
return "";
} else {
return `${Battery.energyRate.toFixed(2)}W`;
}
}
}
}
}
}
@@ -75,72 +75,63 @@ Item {
StyledPopup {
hoverTarget: mouseArea
contentComponent: Rectangle {
id: datePopup
readonly property real margin: 12
implicitWidth: columnLayout.implicitWidth + margin * 2
implicitHeight: columnLayout.implicitHeight + margin * 2
color: Appearance.colors.colSurfaceContainer
radius: Appearance.rounding.small
clip: true
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 {
id: columnLayout
anchors.centerIn: parent
spacing: 4
spacing: 0
Layout.fillWidth: true
// 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
}
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 }
}
// Uptime row
RowLayout {
spacing: 5
StyledText {
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
}
horizontalAlignment: Text.AlignLeft
wrapMode: Text.Wrap
color: Appearance.colors.colOnSurfaceVariant
text: root.todosSection
}
}
}
+41 -50
View File
@@ -70,70 +70,61 @@ Item {
StyledPopup {
hoverTarget: mouseArea
contentComponent: Rectangle {
id: resourcePopup
readonly property real margin: 10
implicitWidth: columnLayout.implicitWidth + margin * 2
implicitHeight: columnLayout.implicitHeight + margin * 2
color: Appearance.colors.colSurfaceContainer
radius: Appearance.rounding.small
clip: true
ColumnLayout {
id: columnLayout
anchors.centerIn: parent
spacing: 4
ColumnLayout {
id: columnLayout
anchors.centerIn: parent
spacing: 4
// Header
RowLayout {
id: header
spacing: 5
// Header
RowLayout {
id: header
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 {
fill: 0
font.weight: Font.Medium
text: root.tooltipHeaderIcon
text: modelData.icon
color: Appearance.colors.colOnSurfaceVariant
iconSize: Appearance.font.pixelSize.large
color: Appearance.colors.colOnSurfaceVariant
}
StyledText {
text: root.tooltipHeaderText
font {
weight: Font.Medium
pixelSize: Appearance.font.pixelSize.normal
}
text: modelData.label
color: Appearance.colors.colOnSurfaceVariant
}
}
// Info rows
Repeater {
model: root.tooltipData
delegate: RowLayout {
spacing: 5
StyledText {
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
}
horizontalAlignment: Text.AlignRight
visible: modelData.value !== ""
color: Appearance.colors.colOnSurfaceVariant
text: modelData.value
}
}
}
}
}
@@ -38,7 +38,8 @@ MouseArea {
StyledPopup {
hoverTarget: root
contentComponent: WeatherPopup {
WeatherPopup {
id: weatherPopup
anchors.centerIn: parent
}
@@ -6,94 +6,83 @@ import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
Rectangle {
id: root
readonly property real margin: 10
implicitWidth: columnLayout.implicitWidth + margin * 2
implicitHeight: columnLayout.implicitHeight + margin * 2
color: Appearance.colors.colSurfaceContainer
radius: Appearance.rounding.small
clip: true
ColumnLayout {
id: columnLayout
spacing: 5
implicitWidth: Math.max(header.implicitWidth, gridLayout.implicitWidth)
implicitHeight: gridLayout.implicitHeight
ColumnLayout {
id: columnLayout
// Header
RowLayout {
id: header
spacing: 5
anchors.centerIn: root
implicitWidth: Math.max(header.implicitWidth, gridLayout.implicitWidth)
implicitHeight: gridLayout.implicitHeight
Layout.alignment: Qt.AlignHCenter
// Header
RowLayout {
id: header
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
}
StyledText {
text: Weather.data.city
font {
weight: Font.Medium
pixelSize: Appearance.font.pixelSize.normal
}
color: Appearance.colors.colOnSurfaceVariant
}
MaterialSymbol {
fill: 0
font.weight: Font.Medium
text: "location_on"
iconSize: Appearance.font.pixelSize.large
color: Appearance.colors.colOnSurfaceVariant
}
// 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
StyledText {
text: Weather.data.city
font {
weight: Font.Medium
pixelSize: Appearance.font.pixelSize.normal
}
color: Appearance.colors.colOnSurfaceVariant
}
}
}
// 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
}
}
}
@@ -10,8 +10,8 @@ import Quickshell.Wayland
LazyLoader {
id: root
property Item hoverTarget
property Component contentComponent
property MouseArea hoverTarget
default property Item contentItem
active: hoverTarget && hoverTarget.containsMouse
@@ -25,13 +25,13 @@ LazyLoader {
anchors.top: !Config.options.bar.bottom
anchors.bottom: Config.options.bar.bottom
implicitWidth: popupContent.implicitWidth
implicitHeight: popupContent.implicitHeight
implicitWidth: popupBackground.implicitWidth
implicitHeight: popupBackground.implicitHeight
margins {
left: root.QsWindow?.mapFromItem(
root.hoverTarget,
(root.hoverTarget.width - popupContent.implicitWidth) / 2, 0
(root.hoverTarget.width - popupBackground.implicitWidth) / 2, 0
).x
top: Appearance.sizes.hyprlandGapsOut
bottom: Appearance.sizes.hyprlandGapsOut
@@ -39,10 +39,16 @@ LazyLoader {
WlrLayershell.namespace: "quickshell:popup"
WlrLayershell.layer: WlrLayer.Overlay
Loader {
id: popupContent
sourceComponent: root.contentComponent
anchors.centerIn: parent
Rectangle {
id: popupBackground
readonly property real margin: 10
color: Appearance.colors.colSurfaceContainer
radius: Appearance.rounding.small
implicitWidth: root.contentItem.implicitWidth + margin * 2
implicitHeight: root.contentItem.implicitHeight + margin * 2
children: [root.contentItem]
}
}
}