diff --git a/Commons/I18n.qml b/Commons/I18n.qml index 31319011..f55b3310 100644 --- a/Commons/I18n.qml +++ b/Commons/I18n.qml @@ -171,14 +171,14 @@ Singleton { // Detect user's favorite locale - languages for (var i = 0; i < Qt.locale().uiLanguages.length; i++) { const fullUserLang = Qt.locale().uiLanguages[i] - + // Try full code match (such as zh CN, en US) if (availableLanguages.includes(fullUserLang)) { Logger.log("I18n", `Exact match found: "${fullUserLang}"`) setLanguage(fullUserLang) return } - + // If full code match fails, try short code matching (such as zh, en) const shortUserLang = fullUserLang.substring(0, 2) if (availableLanguages.includes(shortUserLang)) { @@ -186,7 +186,7 @@ Singleton { setLanguage(shortUserLang) return } - + Logger.log("I18n", `No match for system language: "${fullUserLang}"`) } diff --git a/Modules/Bar/Extras/BarPillHorizontal.qml b/Modules/Bar/Extras/BarPillHorizontal.qml index 4962153c..f3dac570 100644 --- a/Modules/Bar/Extras/BarPillHorizontal.qml +++ b/Modules/Bar/Extras/BarPillHorizontal.qml @@ -49,9 +49,7 @@ Item { Connections { target: root function onTooltipTextChanged() { - if (PanelService.tooltip.visible) { - PanelService.tooltip.updateText(root.tooltipText) - } + TooltipService.updateText(root.tooltipText) } } @@ -221,7 +219,7 @@ Item { onEntered: { hovered = true root.entered() - PanelService.tooltip.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) + TooltipService.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) if (disableOpen || forceClose) { return } @@ -235,7 +233,7 @@ Item { if (!forceOpen && !forceClose) { hide() } - PanelService.tooltip.hide() + TooltipService.hide() } onClicked: function (mouse) { if (mouse.button === Qt.LeftButton) { diff --git a/Modules/Bar/Extras/BarPillVertical.qml b/Modules/Bar/Extras/BarPillVertical.qml index f24ff086..aeb89f49 100644 --- a/Modules/Bar/Extras/BarPillVertical.qml +++ b/Modules/Bar/Extras/BarPillVertical.qml @@ -61,9 +61,7 @@ Item { Connections { target: root function onTooltipTextChanged() { - if (PanelService.tooltip.visible) { - PanelService.tooltip.updateText(root.tooltipText) - } + TooltipService.updateText(root.tooltipText) } } @@ -262,7 +260,7 @@ Item { onEntered: { hovered = true root.entered() - PanelService.tooltip.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) + TooltipService.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) if (disableOpen || forceClose) { return } @@ -276,7 +274,7 @@ Item { if (!forceOpen && !forceClose) { hide() } - PanelService.tooltip.hide() + TooltipService.hide() } onClicked: function (mouse) { if (mouse.button === Qt.LeftButton) { diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index 537c53bd..6f20c335 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -329,11 +329,11 @@ Item { acceptedButtons: Qt.LeftButton onEntered: { if ((windowTitle !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) { - PanelService.tooltip.show(root, windowTitle, BarService.getTooltipDirection()) + TooltipService.show(root, windowTitle, BarService.getTooltipDirection()) } } onExited: { - PanelService.tooltip.hide() + TooltipService.hide() } } } diff --git a/Modules/Bar/Widgets/Clock.qml b/Modules/Bar/Widgets/Clock.qml index 29bce8c6..ae179ebe 100644 --- a/Modules/Bar/Widgets/Clock.qml +++ b/Modules/Bar/Widgets/Clock.qml @@ -114,14 +114,14 @@ Rectangle { hoverEnabled: true onEntered: { 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: { - PanelService.tooltip.hide() + TooltipService.hide() } onClicked: { - PanelService.tooltip.hide() + TooltipService.hide() PanelService.getPanel("calendarPanel")?.toggle(this) } } diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml index 53914fcb..b9e23efd 100644 --- a/Modules/Bar/Widgets/MediaMini.qml +++ b/Modules/Bar/Widgets/MediaMini.qml @@ -376,11 +376,11 @@ Item { onEntered: { if ((tooltipText !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) { - PanelService.tooltip.show(root, tooltipText, BarService.getTooltipDirection()) + TooltipService.show(root, tooltipText, BarService.getTooltipDirection()) } } onExited: { - PanelService.tooltip.hide() + TooltipService.hide() } } } diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml index 5f7d7ed3..e578622f 100644 --- a/Modules/Bar/Widgets/Taskbar.qml +++ b/Modules/Bar/Widgets/Taskbar.qml @@ -98,8 +98,8 @@ Rectangle { } } } - onEntered: PanelService.tooltip.show(taskbarItem, taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app.", BarService.getTooltipDirection()) - onExited: PanelService.tooltip.hide() + onEntered: TooltipService.show(taskbarItem, taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app.", BarService.getTooltipDirection()) + onExited: TooltipService.hide() } } } diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 927ffae9..d1ba924c 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -135,8 +135,8 @@ Rectangle { } } } - onEntered: PanelService.tooltip.show(trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection()) - onExited: PanelService.tooltip.hide() + onEntered: TooltipService.show(trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection()) + onExited: TooltipService.hide() } } } diff --git a/Modules/Dock/Dock.qml b/Modules/Dock/Dock.qml index 6b8d7484..f02f8040 100644 --- a/Modules/Dock/Dock.qml +++ b/Modules/Dock/Dock.qml @@ -474,7 +474,7 @@ Variants { anyAppHovered = true const appName = appButton.appTitle || appButton.appId || "Unknown" const tooltipText = appName.length > 40 ? appName.substring(0, 37) + "..." : appName - PanelService.tooltip.show(appButton, tooltipText, "top") + TooltipService.show(appButton, tooltipText, "top") if (autoHide) { showTimer.stop() hideTimer.stop() @@ -484,7 +484,7 @@ Variants { onExited: { anyAppHovered = false - PanelService.tooltip.hide() + TooltipService.hide() if (autoHide && !dockHovered && !peekHovered && !menuHovered) { hideTimer.restart() } @@ -500,7 +500,7 @@ Variants { // Close any other existing context menu first root.closeAllContextMenus() // Hide tooltip when showing context menu - PanelService.tooltip.hide() + TooltipService.hide() contextMenu.show(appButton, modelData.toplevel || modelData) return } diff --git a/Modules/Tooltip/Tooltip.qml b/Modules/Tooltip/Tooltip.qml index 495649e1..6acb7e61 100644 --- a/Modules/Tooltip/Tooltip.qml +++ b/Modules/Tooltip/Tooltip.qml @@ -14,7 +14,7 @@ PopupWindow { property int padding: Style.marginM property int delay: 0 property int hideDelay: 0 - property int maxWidth: 340 + property int maxWidth: 320 property real scaling: 1.0 property int animationDuration: Style.animationFast property real animationScale: 0.85 @@ -25,7 +25,7 @@ PopupWindow { property real anchorY: 0 property bool isPositioned: false property bool pendingShow: false - property bool animatingOut: false + property bool animatingOut: true visible: false color: Color.transparent @@ -110,11 +110,7 @@ PopupWindow { if (!target || !tipText || tipText === "") return - if (showDelay !== undefined) { - delay = showDelay - } else { - delay = Style.tooltipDelay - } + delay = showDelay // Stop any running timers and animations hideTimer.stop() @@ -144,8 +140,9 @@ PopupWindow { // Function to position and display the tooltip function positionAndShow() { - if (!targetItem || !pendingShow) + if (!targetItem || !targetItem.parent || !pendingShow) { return + } // Get screen dimensions - try multiple methods var screenWidth = Screen.width @@ -393,6 +390,8 @@ PopupWindow { color: Color.mOnSurfaceVariant horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + width: root.maxWidth } } } diff --git a/Services/PanelService.qml b/Services/PanelService.qml index 59db74b4..5d33aeb7 100644 --- a/Services/PanelService.qml +++ b/Services/PanelService.qml @@ -10,9 +10,6 @@ Singleton { // This is not a panel... property var lockScreen: null - // A ref. to our global tooltip - property var tooltip: null - // Panels property var registeredPanels: ({}) property var openedPanel: null diff --git a/Services/TooltipService.qml b/Services/TooltipService.qml new file mode 100644 index 00000000..99a42c80 --- /dev/null +++ b/Services/TooltipService.qml @@ -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) + } + } +}