From e7919f989072009e435a68a798854640f0c667a5 Mon Sep 17 00:00:00 2001 From: Curtis Wensley Date: Mon, 5 Jun 2023 15:30:29 -0700 Subject: [PATCH] Mac: Ensure controls only get mouse events when enabled Wpf: Fix issues disabling some controls when they have a different container --- src/Eto.Mac/Forms/Controls/DrawableHandler.cs | 2 +- src/Eto.Mac/Forms/MacView.cs | 14 ++-- src/Eto.Wpf/Forms/WpfFrameworkElement.cs | 4 +- test/Eto.Test/Sections/UnitTestSection.cs | 11 +-- .../UnitTests/Forms/Controls/ControlTests.cs | 68 +++++++++++++++++++ 5 files changed, 87 insertions(+), 12 deletions(-) diff --git a/src/Eto.Mac/Forms/Controls/DrawableHandler.cs b/src/Eto.Mac/Forms/Controls/DrawableHandler.cs index c4be2dda9d..ecd1d02e87 100644 --- a/src/Eto.Mac/Forms/Controls/DrawableHandler.cs +++ b/src/Eto.Mac/Forms/Controls/DrawableHandler.cs @@ -38,7 +38,7 @@ public override void DrawRect(CGRect dirtyRect) public bool CanFocus { get; set; } - public override bool AcceptsFirstResponder() => CanFocus; + public override bool AcceptsFirstResponder() => CanFocus && Drawable?.Enabled == true; public override NSView HitTest(CGPoint aPoint) { diff --git a/src/Eto.Mac/Forms/MacView.cs b/src/Eto.Mac/Forms/MacView.cs index f9cb8e71b2..0f58dae09e 100644 --- a/src/Eto.Mac/Forms/MacView.cs +++ b/src/Eto.Mac/Forms/MacView.cs @@ -24,7 +24,7 @@ class MouseDelegate : NSObject public void MouseMoved(NSEvent theEvent) { var h = Handler; - if (h == null) return; + if (h == null || !h.Enabled) return; h.Callback.OnMouseMove(h.Widget, MacConversions.GetMouseEvent(h, theEvent, false)); } @@ -32,7 +32,7 @@ public void MouseMoved(NSEvent theEvent) public void MouseEntered(NSEvent theEvent) { var h = Handler; - if (h == null) return; + if (h == null || !h.Enabled) return; h.Callback.OnMouseEnter(h.Widget, MacConversions.GetMouseEvent(h, theEvent, false)); } @@ -45,7 +45,7 @@ public void CursorUpdate(NSEvent theEvent) public void MouseExited(NSEvent theEvent) { var h = Handler; - if (h == null) return; + if (h == null || !h.Enabled) return; h.Callback.OnMouseLeave(h.Widget, MacConversions.GetMouseEvent(h, theEvent, false)); } @@ -84,6 +84,7 @@ public interface IMacViewHandler : IMacControlHandler bool TextInputCancelled { get; set; } bool TextInputImplemented { get; } bool UseNSBoxBackgroundColor { get; set; } + bool Enabled { get; set; } DragEventArgs GetDragEventArgs(NSDraggingInfo info, object customControl); @@ -234,7 +235,7 @@ static void TriggerMouseUp(IntPtr sender, IntPtr sel, IntPtr e) { var obj = Runtime.GetNSObject(sender); - if (MacBase.GetHandler(obj) is IMacViewHandler handler) + if (MacBase.GetHandler(obj) is IMacViewHandler handler && handler.Enabled) { var theEvent = Messaging.GetNSObject(e); handler.TriggerMouseUp(obj, sel, theEvent); @@ -245,7 +246,7 @@ static void TriggerMouseUp(IntPtr sender, IntPtr sel, IntPtr e) static void TriggerMouseWheel(IntPtr sender, IntPtr sel, IntPtr e) { var obj = Runtime.GetNSObject(sender); - if (MacBase.GetHandler(obj) is IMacViewHandler handler) + if (MacBase.GetHandler(obj) is IMacViewHandler handler && handler.Enabled) { var theEvent = Messaging.GetNSObject(e); var args = MacConversions.GetMouseEvent(handler, theEvent, true); @@ -1460,6 +1461,9 @@ public virtual MouseEventArgs TriggerMouseDown(NSObject obj, IntPtr sel, NSEvent // showing context menus, dialogs, etc. MacView.InMouseTrackingLoop = true; + if (!Enabled) + return null; + var args = MacConversions.GetMouseEvent(this, theEvent, false); if (theEvent.ClickCount >= 2) Callback.OnMouseDoubleClick(Widget, args); diff --git a/src/Eto.Wpf/Forms/WpfFrameworkElement.cs b/src/Eto.Wpf/Forms/WpfFrameworkElement.cs index 72feec463c..7a605b96ee 100755 --- a/src/Eto.Wpf/Forms/WpfFrameworkElement.cs +++ b/src/Eto.Wpf/Forms/WpfFrameworkElement.cs @@ -329,8 +329,8 @@ public SizeF GetPreferredSize(SizeF availableSize) public virtual bool Enabled { - get { return Control.IsEnabled; } - set { Control.IsEnabled = value; } + get { return ContainerControl.IsEnabled; } + set { ContainerControl.IsEnabled = value; } } public virtual Cursor Cursor diff --git a/test/Eto.Test/Sections/UnitTestSection.cs b/test/Eto.Test/Sections/UnitTestSection.cs index 2b67eea0cb..af94788bfd 100644 --- a/test/Eto.Test/Sections/UnitTestSection.cs +++ b/test/Eto.Test/Sections/UnitTestSection.cs @@ -7,14 +7,17 @@ namespace Eto.Test.Sections [Section("Automated Tests", "Unit Tests")] public class UnitTestSection : Panel { - UnitTestPanel unitTestPanel; + static UnitTestPanel unitTestPanel; UnitTestRunner runner; public UnitTestSection() { - runner = new UnitTestRunner(((TestApplication)TestApplication.Instance).TestAssemblies); - unitTestPanel = new UnitTestPanel(runner, false); - unitTestPanel.Log += (sender, e) => Log.Write(null, e.Message); + if (unitTestPanel == null) + { + runner = new UnitTestRunner(((TestApplication)TestApplication.Instance).TestAssemblies); + unitTestPanel = new UnitTestPanel(runner, false); + unitTestPanel.Log += (sender, e) => Log.Write(null, e.Message); + } Content = unitTestPanel; } diff --git a/test/Eto.Test/UnitTests/Forms/Controls/ControlTests.cs b/test/Eto.Test/UnitTests/Forms/Controls/ControlTests.cs index 314f26a898..ce35bfc98a 100644 --- a/test/Eto.Test/UnitTests/Forms/Controls/ControlTests.cs +++ b/test/Eto.Test/UnitTests/Forms/Controls/ControlTests.cs @@ -388,5 +388,73 @@ public void ControlsShouldHavePreferredSize(IControlTypeInfo info) Assert.That(containerSize.Height, Is.EqualTo(size.Height + padding.Vertical).Within(0.1), "#2.2 - panel with padding should have correct height"); } + [ManualTest] + [TestCaseSource(nameof(GetControlTypes))] + public void ControlsShouldNotGetMouseOrFocusEventsWhenDisabled(IControlTypeInfo info) + { + ControlsShouldNotGetMouseOrFocusEventsWhenParentDisabled(info, false); + } + + [ManualTest] + [TestCaseSource(nameof(GetControlTypes))] + public void ControlsShouldNotGetMouseOrFocusEventsWhenParentDisabled(IControlTypeInfo info) + { + ControlsShouldNotGetMouseOrFocusEventsWhenParentDisabled(info, true); + } + + public void ControlsShouldNotGetMouseOrFocusEventsWhenParentDisabled(IControlTypeInfo info, bool disableWithParent) + { + bool gotFocus = false; + bool gotMouseDown = false; + bool gotMouseUp = false; + bool gotMouseEnter = false; + bool gotMouseLeave = false; + ManualForm("Click on the Drawable, it should not get focus", form => + { + var control = info.CreatePopulatedControl(); + if (!disableWithParent) + control.Enabled = false; + + control.GotFocus += (sender, e) => + { + Console.WriteLine("GotFocus"); + gotFocus = true; + }; + control.LostFocus += (sender, e) => + { + Console.WriteLine("LostFocus"); + }; + control.MouseDown += (sender, e) => + { + Console.WriteLine("MouseDown"); + gotMouseDown = true; + }; + control.MouseUp += (sender, e) => + { + Console.WriteLine("MouseUp"); + gotMouseUp = true; + }; + control.MouseEnter += (sender, e) => + { + Console.WriteLine("MouseEnter"); + gotMouseEnter = true; + }; + control.MouseLeave += (sender, e) => + { + Console.WriteLine("MouseLeave"); + gotMouseLeave = true; + }; + + var panel = new Panel { Content = control }; + if (disableWithParent) + panel.Enabled = false; + return panel; + }); + Assert.IsFalse(gotFocus, "#1.1 - Control should not be able to get focus"); + Assert.IsFalse(gotMouseEnter, "#1.2 - Got MouseEnter"); + Assert.IsFalse(gotMouseLeave, "#1.3 - Got MouseLeave"); + Assert.IsFalse(gotMouseDown, "#1.4 - Got MouseDown"); + Assert.IsFalse(gotMouseUp, "#1.5 - Got MouseUp"); + } } } \ No newline at end of file