Skip to content

Commit

Permalink
Merge pull request #2497 from cwensley/curtis/mac-fire-mouseleave-whe…
Browse files Browse the repository at this point in the history
…n-disabling-control

Mac: fire MouseLeave if setting a control to disabled while mouse is over
  • Loading branch information
cwensley authored Jun 7, 2023
2 parents 7b37607 + 42b3639 commit 3abe8c7
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/Eto.Mac/Forms/MacView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace Eto.Mac.Forms
class MouseDelegate : NSObject
{
WeakReference widget;
bool entered;

public IMacViewHandler Handler { get { return (IMacViewHandler)widget.Target; } set { widget = new WeakReference(value); } }

Expand All @@ -31,6 +32,7 @@ public void MouseMoved(NSEvent theEvent)
[Export("mouseEntered:")]
public void MouseEntered(NSEvent theEvent)
{
entered = true;
var h = Handler;
if (h == null || !h.Enabled) return;
h.Callback.OnMouseEnter(h.Widget, MacConversions.GetMouseEvent(h, theEvent, false));
Expand All @@ -47,6 +49,7 @@ public void MouseExited(NSEvent theEvent)
var h = Handler;
if (h == null || !h.Enabled) return;
h.Callback.OnMouseLeave(h.Widget, MacConversions.GetMouseEvent(h, theEvent, false));
entered = false;
}

[Export("scrollWheel:")]
Expand All @@ -57,6 +60,21 @@ public void ScrollWheel(NSEvent theEvent)
h.Callback.OnMouseWheel(h.Widget, MacConversions.GetMouseEvent(h, theEvent, true));
}

public void FireMouseLeaveIfNeeded()
{
var h = Handler;
if (h == null || h.Enabled || !entered) return;
entered = false;
Application.Instance.AsyncInvoke(() =>
{
if (!h.Widget.IsDisposed)
{
var theEvent = NSApplication.SharedApplication.CurrentEvent;
h.Callback.OnMouseLeave(h.Widget, MacConversions.GetMouseEvent(h, theEvent, false));
}
});
}

}

public interface IMacViewHandler : IMacControlHandler
Expand Down Expand Up @@ -1017,6 +1035,9 @@ void SetEnabled(bool parentEnabled, bool? newValue)
{
ControlEnabled = newEnabled;
Callback.OnEnabledChanged(Widget, EventArgs.Empty);

if (!newEnabled)
mouseDelegate?.FireMouseLeaveIfNeeded();
}
}

Expand Down
42 changes: 41 additions & 1 deletion test/Eto.Test/UnitTests/Forms/Controls/ControlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ public void ControlsShouldNotGetMouseOrFocusEventsWhenParentDisabled(IControlTyp
bool gotMouseUp = false;
bool gotMouseEnter = false;
bool gotMouseLeave = false;
ManualForm("Click on the Drawable, it should not get focus", form =>
ManualForm("Click on the control, it should not get focus", form =>
{
var control = info.CreatePopulatedControl();
if (!disableWithParent)
Expand Down Expand Up @@ -456,5 +456,45 @@ public void ControlsShouldNotGetMouseOrFocusEventsWhenParentDisabled(IControlTyp
Assert.IsFalse(gotMouseDown, "#1.4 - Got MouseDown");
Assert.IsFalse(gotMouseUp, "#1.5 - Got MouseUp");
}

[ManualTest]
[TestCaseSource(nameof(GetControlTypes))]
public void ControlShouldFireMouseLeaveIfEnteredThenDisabled(IControlTypeInfo<Control> info)
{
bool mouseLeaveCalled = false;
bool mouseEnterCalled = false;
bool mouseLeaveCalledBeforeMouseDown = false;
bool mouseLeaveCalledAfterDisabled = false;
bool mouseDownCalled = false;
ManualForm("Click on the control", form =>
{

var control = info.CreatePopulatedControl();
control.MouseEnter += (sender, e) =>
{
mouseEnterCalled = true;
};
control.MouseLeave += (sender, e) =>
{
mouseLeaveCalled = true;
if (mouseDownCalled)
form.Close();
};
control.MouseDown += (sender, e) =>
{
mouseDownCalled = true;
mouseLeaveCalledBeforeMouseDown = mouseLeaveCalled;
control.Enabled = false;
mouseLeaveCalledAfterDisabled = mouseLeaveCalled;
e.Handled = true;
};
return control;
});
Assert.IsTrue(mouseEnterCalled, "#1.1 - MouseEnter did not get called");
Assert.IsTrue(mouseLeaveCalled, "#1.2 - MouseLeave did not get called");
Assert.IsFalse(mouseLeaveCalledBeforeMouseDown, "#1.3 - MouseLeave should not have been called before MouseDown");
Assert.IsFalse(mouseLeaveCalledAfterDisabled, "#1.4 - MouseLeave should not be called during Enabled=false, but sometime after the MouseDown completes");
Assert.IsTrue(mouseDownCalled, "#1.5 - MouseDown didn't get called. Did you click the control?");
}
}
}

0 comments on commit 3abe8c7

Please sign in to comment.