Selaa lähdekoodia

fix: tooltip 触发优先级

龙腾猫跃 4 kuukautta sitten
vanhempi
sitoutus
212552503e
1 muutettua tiedostoa jossa 88 lisäystä ja 39 poistoa
  1. 88 39
      Wpf/Controls/ModernTooltipService.cs

+ 88 - 39
Wpf/Controls/ModernTooltipService.cs

@@ -8,6 +8,7 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Animation;
 using System.Windows.Media.Effects;
+using System.Windows.Media.Media3D;
 using System.Windows.Threading;
 
 namespace MeloongCore.Wpf;
@@ -86,31 +87,18 @@ public static class ModernTooltipService {
     #region 全局鼠标事件
 
     private static void OnMouseEnter(object sender, MouseEventArgs e) {
-        if (sender is not FrameworkElement owner || !CanShowModernTooltip(owner) || !TryGetToolTip(owner, out _, out _)) return;
-        if (ReferenceEquals(currentOwner, owner) && popup is { IsOpen: true }) {
-            return;
-        }
-
-        if (owner is ComboBox comboBox) HookComboBoxDropDown(comboBox);
-        if (!ReferenceEquals(currentOwner, owner)) HideImmediately();
-
-        currentOwner = owner;
-        lastMousePoint = e.GetPosition(owner);
-        openTimer.Stop();
+        if (sender is not FrameworkElement reference) return;
 
-        var delay = Math.Max(0, ToolTipService.GetInitialShowDelay(owner));
-        if (delay == 0) {
-            Show(owner, lastMousePoint);
-        } else {
-            openTimer.Interval = TimeSpan.FromMilliseconds(delay);
-            openTimer.Start();
-        }
+        reference.Dispatcher.BeginInvoke(new Action(() => RefreshCurrentOwner(reference)), DispatcherPriority.Input);
     }
 
     private static void OnMouseMove(object sender, MouseEventArgs e) {
-        if (sender is not FrameworkElement owner || !ReferenceEquals(owner, currentOwner)) return;
+        if (sender is not FrameworkElement reference) return;
+
+        RefreshCurrentOwner(reference);
+        if (currentOwner is not FrameworkElement owner) return;
 
-        lastMousePoint = e.GetPosition(owner);
+        lastMousePoint = Mouse.GetPosition(owner);
         if (e.LeftButton == MouseButtonState.Pressed && !IsPointInside(owner, lastMousePoint)) {
             Close(true);
             return;
@@ -126,6 +114,12 @@ public static class ModernTooltipService {
         var point = e.GetPosition(owner);
         if (e.LeftButton == MouseButtonState.Pressed && IsPointInside(owner, point)) return;
 
+        var nextOwner = FindCurrentTooltipOwner(owner);
+        if (nextOwner is not null && !ReferenceEquals(nextOwner, owner)) {
+            BeginShow(nextOwner, Mouse.GetPosition(nextOwner));
+            return;
+        }
+
         Close(true);
     }
 
@@ -163,10 +157,83 @@ public static class ModernTooltipService {
         return content is not null && (content is not string text || text.Length > 0);
     }
 
+    private static void RefreshCurrentOwner(FrameworkElement reference) {
+        var owner = FindCurrentTooltipOwner(reference);
+        if (owner is null) return;
+
+        BeginShow(owner, Mouse.GetPosition(owner));
+    }
+
+    private static FrameworkElement? FindCurrentTooltipOwner(FrameworkElement reference) {
+        var owner = FindTooltipOwnerInAncestors(Mouse.DirectlyOver as DependencyObject);
+        if (owner is not null) return owner;
+
+        return reference.InputHitTest(Mouse.GetPosition(reference)) is DependencyObject hit
+            ? FindTooltipOwnerInAncestors(hit)
+            : null;
+    }
+
+    private static FrameworkElement? FindTooltipOwnerInAncestors(DependencyObject? start) {
+        for (var current = start; current is not null;) {
+            if (current is FrameworkElement owner && CanShowModernTooltip(owner) && TryGetToolTip(owner, out _, out _)) {
+                return owner;
+            }
+
+            DependencyObject? parent = null;
+            if (current is Visual or Visual3D) parent = VisualTreeHelper.GetParent(current);
+
+            current = parent ?? LogicalTreeHelper.GetParent(current);
+        }
+
+        return null;
+    }
+
+    private static void BeginShow(FrameworkElement owner, Point point) {
+        if (!CanShowModernTooltip(owner) || !TryGetToolTip(owner, out _, out _)) return;
+
+        if (owner is ComboBox comboBox) HookComboBoxDropDown(comboBox);
+        if (ReferenceEquals(currentOwner, owner)) {
+            lastMousePoint = point;
+            if (popup is not { IsOpen: true } && !openTimer.IsEnabled) StartOpenTimer(owner);
+            return;
+        }
+
+        HideImmediately();
+        currentOwner = owner;
+        lastMousePoint = point;
+        StartOpenTimer(owner);
+    }
+
+    private static void StartOpenTimer(FrameworkElement owner) {
+        openTimer.Stop();
+
+        var delay = Math.Max(0, ToolTipService.GetInitialShowDelay(owner));
+        if (delay == 0) {
+            Show(owner, lastMousePoint);
+        } else {
+            openTimer.Interval = TimeSpan.FromMilliseconds(delay);
+            openTimer.Start();
+        }
+    }
+
     private static void Show(FrameworkElement owner, Point point) {
         if (!ReferenceEquals(owner, currentOwner) || !CanShowModernTooltip(owner) || !TryGetToolTip(owner, out var content, out var sourceToolTip)) return;
 
-        EnsurePopup();
+        if (popup is null) {
+            popupScale = new ScaleTransform(0.97, 0.97);
+            popupCard = new Border { 
+                Background = backgroundBrush, BorderBrush = borderBrush, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(8), 
+                MaxWidth = 700, SnapsToDevicePixels = true, UseLayoutRounding = true, 
+                RenderTransform = popupScale, RenderTransformOrigin = new Point(0, 0), 
+                Effect = new DropShadowEffect { Opacity = shadowOpacity, BlurRadius = shadowRadius, ShadowDepth = 0, Color = Colors.Black } };
+
+            var root = new Grid { Margin = new Thickness(shadowRadius + 1), SnapsToDevicePixels = true, UseLayoutRounding = true };
+            root.Children.Add(popupCard);
+
+            popup = new Popup { 
+                AllowsTransparency = true, IsHitTestVisible = false, StaysOpen = true, PopupAnimation = PopupAnimation.None, 
+                Placement = PlacementMode.Relative, Child = root };
+        }
 
         popup!.PlacementTarget = owner;
         popupCard!.DataContext = sourceToolTip?.DataContext ?? owner.DataContext;
@@ -246,24 +313,6 @@ public static class ModernTooltipService {
 
     #region Popup 创建、定位与动画
 
-    private static void EnsurePopup() {
-        if (popup is not null) return;
-
-        popupScale = new ScaleTransform(0.97, 0.97);
-        popupCard = new Border { 
-            Background = backgroundBrush, BorderBrush = borderBrush, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(8), 
-            MaxWidth = 700, SnapsToDevicePixels = true, UseLayoutRounding = true, 
-            RenderTransform = popupScale, RenderTransformOrigin = new Point(0, 0), 
-            Effect = new DropShadowEffect { Opacity = shadowOpacity, BlurRadius = shadowRadius, ShadowDepth = 0, Color = Colors.Black } };
-
-        var root = new Grid { Margin = new Thickness(shadowRadius + 1), SnapsToDevicePixels = true, UseLayoutRounding = true };
-        root.Children.Add(popupCard);
-
-        popup = new Popup { 
-            AllowsTransparency = true, IsHitTestVisible = false, StaysOpen = true, PopupAnimation = PopupAnimation.None, 
-            Placement = PlacementMode.Relative, Child = root };
-    }
-
     private static void UpdatePosition(FrameworkElement owner, Point point) {
         popup!.PlacementTarget = owner;
         popup.HorizontalOffset = Math.Round(point.X + 15);