Tooltips: proper tooltip service

This commit is contained in:
ItsLemmy
2025-09-28 10:40:15 -04:00
parent 8dda007847
commit fc1742e167
15 changed files with 151 additions and 50 deletions
+3 -3
View File
@@ -171,14 +171,14 @@ Singleton {
// Detect user's favorite locale - languages // Detect user's favorite locale - languages
for (var i = 0; i < Qt.locale().uiLanguages.length; i++) { for (var i = 0; i < Qt.locale().uiLanguages.length; i++) {
const fullUserLang = Qt.locale().uiLanguages[i] const fullUserLang = Qt.locale().uiLanguages[i]
// Try full code match (such as zh CN, en US) // Try full code match (such as zh CN, en US)
if (availableLanguages.includes(fullUserLang)) { if (availableLanguages.includes(fullUserLang)) {
Logger.log("I18n", `Exact match found: "${fullUserLang}"`) Logger.log("I18n", `Exact match found: "${fullUserLang}"`)
setLanguage(fullUserLang) setLanguage(fullUserLang)
return return
} }
// If full code match fails, try short code matching (such as zh, en) // If full code match fails, try short code matching (such as zh, en)
const shortUserLang = fullUserLang.substring(0, 2) const shortUserLang = fullUserLang.substring(0, 2)
if (availableLanguages.includes(shortUserLang)) { if (availableLanguages.includes(shortUserLang)) {
@@ -186,7 +186,7 @@ Singleton {
setLanguage(shortUserLang) setLanguage(shortUserLang)
return return
} }
Logger.log("I18n", `No match for system language: "${fullUserLang}"`) Logger.log("I18n", `No match for system language: "${fullUserLang}"`)
} }
+3 -5
View File
@@ -49,9 +49,7 @@ Item {
Connections { Connections {
target: root target: root
function onTooltipTextChanged() { function onTooltipTextChanged() {
if (PanelService.tooltip.visible) { TooltipService.updateText(root.tooltipText)
PanelService.tooltip.updateText(root.tooltipText)
}
} }
} }
@@ -221,7 +219,7 @@ Item {
onEntered: { onEntered: {
hovered = true hovered = true
root.entered() root.entered()
PanelService.tooltip.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) TooltipService.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong)
if (disableOpen || forceClose) { if (disableOpen || forceClose) {
return return
} }
@@ -235,7 +233,7 @@ Item {
if (!forceOpen && !forceClose) { if (!forceOpen && !forceClose) {
hide() hide()
} }
PanelService.tooltip.hide() TooltipService.hide()
} }
onClicked: function (mouse) { onClicked: function (mouse) {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
+3 -5
View File
@@ -61,9 +61,7 @@ Item {
Connections { Connections {
target: root target: root
function onTooltipTextChanged() { function onTooltipTextChanged() {
if (PanelService.tooltip.visible) { TooltipService.updateText(root.tooltipText)
PanelService.tooltip.updateText(root.tooltipText)
}
} }
} }
@@ -262,7 +260,7 @@ Item {
onEntered: { onEntered: {
hovered = true hovered = true
root.entered() root.entered()
PanelService.tooltip.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) TooltipService.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong)
if (disableOpen || forceClose) { if (disableOpen || forceClose) {
return return
} }
@@ -276,7 +274,7 @@ Item {
if (!forceOpen && !forceClose) { if (!forceOpen && !forceClose) {
hide() hide()
} }
PanelService.tooltip.hide() TooltipService.hide()
} }
onClicked: function (mouse) { onClicked: function (mouse) {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
+2 -2
View File
@@ -329,11 +329,11 @@ Item {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onEntered: { onEntered: {
if ((windowTitle !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) { if ((windowTitle !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) {
PanelService.tooltip.show(root, windowTitle, BarService.getTooltipDirection()) TooltipService.show(root, windowTitle, BarService.getTooltipDirection())
} }
} }
onExited: { onExited: {
PanelService.tooltip.hide() TooltipService.hide()
} }
} }
} }
+3 -3
View File
@@ -114,14 +114,14 @@ Rectangle {
hoverEnabled: true hoverEnabled: true
onEntered: { onEntered: {
if (!PanelService.getPanel("calendarPanel")?.active) { if (!PanelService.getPanel("calendarPanel")?.active) {
PanelService.tooltip.show(root, I18n.tr("clock.tooltip"), BarService.getTooltipDirection()) TooltipService.show(root, I18n.tr("clock.tooltip"), BarService.getTooltipDirection())
} }
} }
onExited: { onExited: {
PanelService.tooltip.hide() TooltipService.hide()
} }
onClicked: { onClicked: {
PanelService.tooltip.hide() TooltipService.hide()
PanelService.getPanel("calendarPanel")?.toggle(this) PanelService.getPanel("calendarPanel")?.toggle(this)
} }
} }
+2 -2
View File
@@ -376,11 +376,11 @@ Item {
onEntered: { onEntered: {
if ((tooltipText !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) { if ((tooltipText !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) {
PanelService.tooltip.show(root, tooltipText, BarService.getTooltipDirection()) TooltipService.show(root, tooltipText, BarService.getTooltipDirection())
} }
} }
onExited: { onExited: {
PanelService.tooltip.hide() TooltipService.hide()
} }
} }
} }
+2 -2
View File
@@ -98,8 +98,8 @@ Rectangle {
} }
} }
} }
onEntered: PanelService.tooltip.show(taskbarItem, taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app.", BarService.getTooltipDirection()) onEntered: TooltipService.show(taskbarItem, taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app.", BarService.getTooltipDirection())
onExited: PanelService.tooltip.hide() onExited: TooltipService.hide()
} }
} }
} }
+2 -2
View File
@@ -135,8 +135,8 @@ Rectangle {
} }
} }
} }
onEntered: PanelService.tooltip.show(trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection()) onEntered: TooltipService.show(trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection())
onExited: PanelService.tooltip.hide() onExited: TooltipService.hide()
} }
} }
} }
+3 -3
View File
@@ -474,7 +474,7 @@ Variants {
anyAppHovered = true anyAppHovered = true
const appName = appButton.appTitle || appButton.appId || "Unknown" const appName = appButton.appTitle || appButton.appId || "Unknown"
const tooltipText = appName.length > 40 ? appName.substring(0, 37) + "..." : appName const tooltipText = appName.length > 40 ? appName.substring(0, 37) + "..." : appName
PanelService.tooltip.show(appButton, tooltipText, "top") TooltipService.show(appButton, tooltipText, "top")
if (autoHide) { if (autoHide) {
showTimer.stop() showTimer.stop()
hideTimer.stop() hideTimer.stop()
@@ -484,7 +484,7 @@ Variants {
onExited: { onExited: {
anyAppHovered = false anyAppHovered = false
PanelService.tooltip.hide() TooltipService.hide()
if (autoHide && !dockHovered && !peekHovered && !menuHovered) { if (autoHide && !dockHovered && !peekHovered && !menuHovered) {
hideTimer.restart() hideTimer.restart()
} }
@@ -500,7 +500,7 @@ Variants {
// Close any other existing context menu first // Close any other existing context menu first
root.closeAllContextMenus() root.closeAllContextMenus()
// Hide tooltip when showing context menu // Hide tooltip when showing context menu
PanelService.tooltip.hide() TooltipService.hide()
contextMenu.show(appButton, modelData.toplevel || modelData) contextMenu.show(appButton, modelData.toplevel || modelData)
return return
} }
+7 -8
View File
@@ -14,7 +14,7 @@ PopupWindow {
property int padding: Style.marginM property int padding: Style.marginM
property int delay: 0 property int delay: 0
property int hideDelay: 0 property int hideDelay: 0
property int maxWidth: 340 property int maxWidth: 320
property real scaling: 1.0 property real scaling: 1.0
property int animationDuration: Style.animationFast property int animationDuration: Style.animationFast
property real animationScale: 0.85 property real animationScale: 0.85
@@ -25,7 +25,7 @@ PopupWindow {
property real anchorY: 0 property real anchorY: 0
property bool isPositioned: false property bool isPositioned: false
property bool pendingShow: false property bool pendingShow: false
property bool animatingOut: false property bool animatingOut: true
visible: false visible: false
color: Color.transparent color: Color.transparent
@@ -110,11 +110,7 @@ PopupWindow {
if (!target || !tipText || tipText === "") if (!target || !tipText || tipText === "")
return return
if (showDelay !== undefined) { delay = showDelay
delay = showDelay
} else {
delay = Style.tooltipDelay
}
// Stop any running timers and animations // Stop any running timers and animations
hideTimer.stop() hideTimer.stop()
@@ -144,8 +140,9 @@ PopupWindow {
// Function to position and display the tooltip // Function to position and display the tooltip
function positionAndShow() { function positionAndShow() {
if (!targetItem || !pendingShow) if (!targetItem || !targetItem.parent || !pendingShow) {
return return
}
// Get screen dimensions - try multiple methods // Get screen dimensions - try multiple methods
var screenWidth = Screen.width var screenWidth = Screen.width
@@ -393,6 +390,8 @@ PopupWindow {
color: Color.mOnSurfaceVariant color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
width: root.maxWidth
} }
} }
} }
-3
View File
@@ -10,9 +10,6 @@ Singleton {
// This is not a panel... // This is not a panel...
property var lockScreen: null property var lockScreen: null
// A ref. to our global tooltip
property var tooltip: null
// Panels // Panels
property var registeredPanels: ({}) property var registeredPanels: ({})
property var openedPanel: null property var openedPanel: null
+114
View File
@@ -0,0 +1,114 @@
pragma Singleton
import QtQuick
import Quickshell
import qs.Commons
import qs.Modules.Tooltip
Singleton {
id: root
property var activeTooltip: null
property var pendingTooltip: null // Track tooltip being created
property Component tooltipComponent: Component {
Tooltip {}
}
function show(target, text, direction, delay) {
// Don't create if no text
if (!target || !text) {
Logger.log("Tooltip", "No target or text")
return
}
// If we have a pending tooltip for a different target, cancel it
if (pendingTooltip && pendingTooltip.targetItem !== target) {
pendingTooltip.hideImmediately()
pendingTooltip.destroy()
pendingTooltip = null
}
// If we have an active tooltip for a different target, hide it
if (activeTooltip && activeTooltip.targetItem !== target) {
activeTooltip.hideImmediately()
// Don't destroy immediately - let it clean itself up
activeTooltip = null
}
// If we already have a tooltip for this target, just update it
if (activeTooltip && activeTooltip.targetItem === target) {
activeTooltip.updateText(text)
return activeTooltip
}
// Create new tooltip instance
const newTooltip = tooltipComponent.createObject(null)
if (newTooltip) {
// Track as pending until it's visible
pendingTooltip = newTooltip
// Connect cleanup when tooltip hides
newTooltip.visibleChanged.connect(() => {
if (!newTooltip.visible) {
// Clean up after a delay to avoid interfering with new tooltips
Qt.callLater(() => {
if (newTooltip && !newTooltip.visible) {
if (activeTooltip === newTooltip) {
activeTooltip = null
}
if (pendingTooltip === newTooltip) {
pendingTooltip = null
}
newTooltip.destroy()
}
})
} else {
// Tooltip is now visible, move from pending to active
if (pendingTooltip === newTooltip) {
activeTooltip = newTooltip
pendingTooltip = null
}
}
})
// Show the tooltip
newTooltip.show(target, text, direction || "auto", delay || Style.tooltipDelay)
return newTooltip
} else {
Logger.error("Tooltip", "Failed to create tooltip instance")
}
return null
}
function hide() {
if (pendingTooltip) {
pendingTooltip.hide()
}
if (activeTooltip) {
activeTooltip.hide()
}
}
function hideImmediately() {
if (pendingTooltip) {
pendingTooltip.hideImmediately()
pendingTooltip.destroy()
pendingTooltip = null
}
if (activeTooltip) {
activeTooltip.hideImmediately()
activeTooltip.destroy()
activeTooltip = null
}
}
function updateText(newText) {
if (activeTooltip) {
activeTooltip.updateText(newText)
}
}
}
+4 -4
View File
@@ -139,18 +139,18 @@ Rectangle {
onEntered: { onEntered: {
root.hovered = true root.hovered = true
if (tooltipText) { if (tooltipText) {
PanelService.tooltip.show(root, root.tooltipText) TooltipService.show(root, root.tooltipText)
} }
} }
onExited: { onExited: {
root.hovered = false root.hovered = false
if (tooltipText) { if (tooltipText) {
PanelService.tooltip.hide() TooltipService.hide()
} }
} }
onPressed: mouse => { onPressed: mouse => {
if (tooltipText) { if (tooltipText) {
PanelService.tooltip.hide() TooltipService.hide()
} }
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
root.clicked() root.clicked()
@@ -164,7 +164,7 @@ Rectangle {
onCanceled: { onCanceled: {
root.hovered = false root.hovered = false
if (tooltipText) { if (tooltipText) {
PanelService.tooltip.hide() TooltipService.hide()
} }
} }
} }
+3 -3
View File
@@ -73,20 +73,20 @@ Rectangle {
onEntered: { onEntered: {
hovering = root.enabled ? true : false hovering = root.enabled ? true : false
if (tooltipText) { if (tooltipText) {
PanelService.tooltip.show(parent, tooltipText, tooltipDirection) TooltipService.show(parent, tooltipText, tooltipDirection)
} }
root.entered() root.entered()
} }
onExited: { onExited: {
hovering = false hovering = false
if (tooltipText) { if (tooltipText) {
PanelService.tooltip.hide() TooltipService.hide()
} }
root.exited() root.exited()
} }
onClicked: function (mouse) { onClicked: function (mouse) {
if (tooltipText) { if (tooltipText) {
PanelService.tooltip.hide() TooltipService.hide()
} }
if (!root.enabled && !allowClickWhenDisabled) { if (!root.enabled && !allowClickWhenDisabled) {
return return
-5
View File
@@ -51,10 +51,6 @@ ShellRoot {
Bar {} Bar {}
Dock {} Dock {}
Tooltip {
id: globalTooltip
}
Notification { Notification {
id: notification id: notification
} }
@@ -120,7 +116,6 @@ ShellRoot {
Component.onCompleted: { Component.onCompleted: {
// Save a ref. to our lockScreen so we can access it easily // Save a ref. to our lockScreen so we can access it easily
PanelService.lockScreen = lockScreen PanelService.lockScreen = lockScreen
PanelService.tooltip = globalTooltip
BarWidgetRegistry.init() BarWidgetRegistry.init()
} }