Update default mouse activation to LeftButtonReleased

Changed default Activate binding from LeftButtonPressed to LeftButtonReleased (issue #4674). Updated all affected tests to reflect new behavior: Activating now fires on release, not press. Adjusted assertions, test names, and mouse event simulation. Custom binding logic now uses ReplaceCommands for Released. Comments clarify new default; focus still set on press, activation on release.
This commit is contained in:
Tig
2026-02-03 18:13:21 -07:00
parent d2f7271739
commit 83b579ec7b
7 changed files with 70 additions and 56 deletions

View File

@@ -128,14 +128,14 @@ public class MouseHoldRepeatTests (ITestOutputHelper output)
// Act - Press button
view.NewMouseEvent (mouse);
Assert.Equal (1, activatingCount); // Should fire on press
Assert.Equal (0, activatingCount); // Default changed: should NOT fire on press (issue #4674)
// Act - Release button
mouse.Flags = MouseFlags.LeftButtonReleased;
mouse.Handled = false;
view.NewMouseEvent (mouse);
// Assert - Activating should be raised exactly once
// Assert - Activating should be raised exactly once (on release)
Assert.Equal (1, activatingCount);
view.Dispose ();
@@ -175,7 +175,7 @@ public class MouseHoldRepeatTests (ITestOutputHelper output)
// Now press
view.NewMouseEvent (mouse);
Assert.Equal (1, activatingCount); // Should fire on press
Assert.Equal (0, activatingCount); // Default changed: should NOT fire on press (issue #4674)
// Act - Release button
mouse.Flags = MouseFlags.LeftButtonReleased;
@@ -530,7 +530,7 @@ public class MouseHoldRepeatTests (ITestOutputHelper output)
ScreenPosition = new (0, 0)
});
Assert.Equal (1, activatingCount); // Should fire on press
Assert.Equal (0, activatingCount); // Default changed: should NOT fire on press (issue #4674)
// Act - Release at (0, 0) - synthesizes Clicked event
app.InjectMouse (
@@ -540,7 +540,7 @@ public class MouseHoldRepeatTests (ITestOutputHelper output)
ScreenPosition = new (0, 0)
});
// Assert - Activating should still be 1 (not fired again on Clicked)
// Assert - Activating should fire once on release
Assert.Equal (1, activatingCount);
(runnable as View)?.Dispose ();

View File

@@ -37,8 +37,8 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
(runnable as View)?.Add (view);
app.Begin (runnable);
// Add custom binding for Released
view.MouseBindings.Add (MouseFlags.LeftButtonReleased, Command.Accept);
// Replace default Released binding (Activate) with custom binding (Accept)
view.MouseBindings.ReplaceCommands (MouseFlags.LeftButtonReleased, Command.Accept);
var commandInvoked = false;
view.Accepting += (_, _) => commandInvoked = true;
@@ -155,8 +155,8 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
(runnable as View)?.Add (view);
app.Begin (runnable);
// Add custom binding for Released
view.MouseBindings.Add (MouseFlags.LeftButtonReleased, Command.Accept);
// Replace default Released binding (Activate) with custom binding (Accept)
view.MouseBindings.ReplaceCommands (MouseFlags.LeftButtonReleased, Command.Accept);
var commandInvoked = false;
view.Accepting += (_, _) => commandInvoked = true;
@@ -218,7 +218,7 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
#region Phase 1.3: Released Binding vs. Default Behavior
[Fact]
public void LeftButtonReleased_NoCustomBinding_DoesNotInvokeAnyCommand ()
public void LeftButtonReleased_DefaultBinding_InvokesActivate ()
{
// Arrange
VirtualTimeProvider time = new ();
@@ -238,7 +238,7 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
(runnable as View)?.Add (view);
app.Begin (runnable);
// DO NOT add custom binding for Released - test default behavior
// Use default binding - test new default behavior (issue #4674)
var activatingInvoked = false;
var acceptingInvoked = false;
view.Activating += (_, _) => activatingInvoked = true;
@@ -247,21 +247,20 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
// Act - Press then Release
app.InjectMouse (new Mouse { Flags = MouseFlags.LeftButtonPressed, ScreenPosition = new Point (0, 0) });
// Reset flag from Pressed event (which should have fired Activating)
activatingInvoked = false;
// Press should NOT activate (default changed to Released)
Assert.False (activatingInvoked, "Pressed should not invoke Activating with default bindings");
app.InjectMouse (new Mouse { Flags = MouseFlags.LeftButtonReleased, ScreenPosition = new Point (0, 0) });
// Assert - Released should not invoke any commands by default
// (only Pressed or Clicked invoke commands by default)
Assert.False (activatingInvoked, "Released event should not invoke Activating without custom binding");
Assert.False (acceptingInvoked, "Released event should not invoke Accepting without custom binding");
// Assert - Released SHOULD invoke Activate by default (new behavior)
Assert.True (activatingInvoked, "Released event should invoke Activating with default binding");
Assert.False (acceptingInvoked, "Released event should not invoke Accepting by default");
(runnable as View)?.Dispose ();
}
[Fact]
public void LeftButtonReleased_CustomBinding_CoexistsWithPressedBinding ()
public void LeftButtonPressed_CustomBinding_CoexistsWithDefaultReleasedBinding ()
{
// Arrange
VirtualTimeProvider time = new ();
@@ -281,8 +280,8 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
(runnable as View)?.Add (view);
app.Begin (runnable);
// Add Released binding alongside default Pressed binding
view.MouseBindings.Add (MouseFlags.LeftButtonReleased, Command.Accept);
// Add custom Pressed binding alongside default Released binding (default changed to Released, issue #4674)
view.MouseBindings.Add (MouseFlags.LeftButtonPressed, Command.Accept);
var activatingInvoked = false;
var acceptingInvoked = false;
@@ -291,11 +290,13 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
// Act - Press then Release
app.InjectMouse (new Mouse { Flags = MouseFlags.LeftButtonPressed, ScreenPosition = new Point (0, 0) });
Assert.True (acceptingInvoked, "Command.Accept (custom Pressed binding) should have been invoked");
app.InjectMouse (new Mouse { Flags = MouseFlags.LeftButtonReleased, ScreenPosition = new Point (0, 0) });
// Assert - Both Pressed and Released bindings should fire
Assert.True (activatingInvoked, "Command.Activate (Pressed) should have been invoked");
Assert.True (acceptingInvoked, "Command.Accept (Released) should have been invoked");
Assert.True (acceptingInvoked, "Command.Accept (Pressed) should have been invoked");
Assert.True (activatingInvoked, "Command.Activate (default Released) should have been invoked");
(runnable as View)?.Dispose ();
}
@@ -321,8 +322,8 @@ public class MouseReleasedBindingTests (ITestOutputHelper output)
(runnable as View)?.Add (view);
app.Begin (runnable);
// Add Released binding with multiple commands
view.MouseBindings.Add (MouseFlags.LeftButtonReleased, Command.Accept, Command.HotKey);
// Replace default Released binding with multiple commands
view.MouseBindings.ReplaceCommands (MouseFlags.LeftButtonReleased, Command.Accept, Command.HotKey);
var acceptingInvoked = false;
var hotKeyInvoked = false;

View File

@@ -11,8 +11,9 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
{
var testView = new View ();
Assert.Contains (MouseFlags.LeftButtonPressed, testView.MouseBindings.GetAllFromCommands (Command.Activate));
Assert.Contains (MouseFlags.LeftButtonPressed | MouseFlags.Ctrl, testView.MouseBindings.GetAllFromCommands (Command.Context));
// Default bindings changed to Released (issue #4674)
Assert.Contains (MouseFlags.LeftButtonReleased, testView.MouseBindings.GetAllFromCommands (Command.Activate));
Assert.Contains (MouseFlags.LeftButtonReleased | MouseFlags.Ctrl, testView.MouseBindings.GetAllFromCommands (Command.Context));
Assert.Equal (2, testView.MouseBindings.GetBindings ().Count ());
@@ -21,11 +22,11 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
MouseHoldRepeat = MouseFlags.LeftButtonReleased
};
Assert.Contains (MouseFlags.LeftButtonPressed, testView.MouseBindings.GetAllFromCommands (Command.Activate));
Assert.Contains (MouseFlags.LeftButtonPressed | MouseFlags.Ctrl, testView.MouseBindings.GetAllFromCommands (Command.Context));
// With MouseHoldRepeat set, the Released binding is used for repeat behavior
Assert.Contains (MouseFlags.LeftButtonReleased, testView.MouseBindings.GetAllFromCommands (Command.Activate));
Assert.Contains (MouseFlags.LeftButtonReleased | MouseFlags.Ctrl, testView.MouseBindings.GetAllFromCommands (Command.Context));
Assert.Equal (3, testView.MouseBindings.GetBindings ().Count ());
Assert.Equal (2, testView.MouseBindings.GetBindings ().Count ());
}
@@ -69,7 +70,7 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
}
[Fact]
public void LeftButtonPressed_RaisesActivating_WhenCanFocus ()
public void LeftButtonReleased_RaisesActivating_WhenCanFocus ()
{
// Arrange
View superView = new () { CanFocus = true, Width = 20, Height = 20 };
@@ -87,14 +88,21 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
int acceptingCount = 0;
view.Activating += (_, _) => acceptingCount++;
Mouse mouse = new ()
// Act - Press then Release (default behavior changed to Released, issue #4674)
Mouse pressedMouse = new ()
{
Position = new (5, 5),
Flags = MouseFlags.LeftButtonPressed
};
view.NewMouseEvent (pressedMouse);
Assert.Equal (0, acceptingCount); // Should NOT activate on press
// Act
view.NewMouseEvent (mouse);
Mouse releasedMouse = new ()
{
Position = new (5, 5),
Flags = MouseFlags.LeftButtonReleased
};
view.NewMouseEvent (releasedMouse);
// Assert
Assert.Equal (1, acceptingCount);
@@ -142,7 +150,7 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
{
var superView = new View { CanFocus = true, Height = 1, Width = 15 };
var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
var testView = new View { CanFocus = canFocus, X = 4, Width = 4, Height = 1 };
var testView = new View { CanFocus = canFocus, X = 4, Width = 4, Height = 1, MouseHighlightStates = MouseState.Pressed };
superView.Add (focusedView, testView);
focusedView.SetFocus ();
@@ -156,6 +164,7 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
testView.SetFocus ();
}
// Note: Pressed still sets focus (via HandleAutoGrabPress), even though activation moved to Released
testView.NewMouseEvent (new () { Timestamp = DateTime.Now, Position = new Point (0, 0), Flags = MouseFlags.LeftButtonPressed });
Assert.True (superView.HasFocus);
Assert.Equal (expectedHasFocus, testView.HasFocus);
@@ -166,7 +175,7 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
[InlineData (false, false, 1)]
[InlineData (true, false, 1)]
[InlineData (true, true, 1)]
public void LeftButtonPressed_Raises_Activating (bool canFocus, bool setFocus, int expectedAcceptingCount)
public void LeftButtonReleased_Raises_Activating (bool canFocus, bool setFocus, int expectedAcceptingCount)
{
var superView = new View { CanFocus = true, Height = 1, Width = 15 };
var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
@@ -187,7 +196,11 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
var acceptingCount = 0;
testView.Activating += (sender, args) => acceptingCount++;
// Default behavior changed to Released (issue #4674)
testView.NewMouseEvent (new () { Timestamp = DateTime.Now, Position = new Point (0, 0), Flags = MouseFlags.LeftButtonPressed });
Assert.Equal (0, acceptingCount); // Should NOT activate on press
testView.NewMouseEvent (new () { Timestamp = DateTime.Now, Position = new Point (0, 0), Flags = MouseFlags.LeftButtonReleased });
Assert.True (superView.HasFocus);
Assert.Equal (expectedAcceptingCount, acceptingCount);
}

View File

@@ -308,10 +308,10 @@ public class ViewCommandTests
}
[Fact]
public void LeftButtonPressed_Invokes_Activate_Command ()
public void LeftButtonReleased_Invokes_Activate_Command ()
{
var view = new ViewEventTester ();
view.NewMouseEvent (new () { Flags = MouseFlags.LeftButtonPressed, Position = Point.Empty, View = view });
view.NewMouseEvent (new () { Flags = MouseFlags.LeftButtonReleased, Position = Point.Empty, View = view });
Assert.Equal (1, view.OnActivatingCount);
}

View File

@@ -399,14 +399,14 @@ public class ButtonTests
injector.InjectMouse (new () { ScreenPosition = clickPos, Flags = MouseFlags.LeftButtonReleased, Timestamp = baseTime.AddMilliseconds (50) }, options);
Assert.Equal (1, acceptingCount);
Assert.Equal (2, activatingCount);
Assert.Equal (1, activatingCount);
// Second click - more than 500ms later to avoid double-click detection
injector.InjectMouse (new () { ScreenPosition = clickPos, Flags = MouseFlags.LeftButtonPressed, Timestamp = baseTime.AddMilliseconds (600) }, options);
injector.InjectMouse (new () { ScreenPosition = clickPos, Flags = MouseFlags.LeftButtonReleased, Timestamp = baseTime.AddMilliseconds (650) }, options);
Assert.Equal (2, acceptingCount);
Assert.Equal (4, activatingCount);
Assert.Equal (2, activatingCount);
}
/// <summary>
@@ -463,7 +463,7 @@ public class ButtonTests
// Activating: 1 from Pressed + 1 from Clicked = 2
// Accepting: 1 from Clicked only = 1
Assert.Equal (2, activatingCount);
Assert.Equal (1, activatingCount);
Assert.Equal (1, acceptingCount);
// Second click - more than 500ms later to avoid double-click detection
@@ -472,7 +472,7 @@ public class ButtonTests
// Activating: previous 2 + 1 from Pressed + 1 from Clicked = 4
// Accepting: previous 1 + 1 from Clicked = 2
Assert.Equal (4, activatingCount);
Assert.Equal (2, activatingCount);
Assert.Equal (2, acceptingCount);
// Third click - verify it continues to work
@@ -483,7 +483,7 @@ public class ButtonTests
// Activating: previous 4 + 1 from Pressed + 1 from Clicked = 6
// Accepting: previous 2 + 1 from Clicked = 3
Assert.Equal (6, activatingCount);
Assert.Equal (3, activatingCount);
Assert.Equal (3, acceptingCount);
// Fourth click - verify consistency
@@ -494,7 +494,7 @@ public class ButtonTests
// Activating: previous 6 + 1 from Pressed + 1 from Clicked = 8
// Accepting: previous 3 + 1 from Clicked = 4
Assert.Equal (8, activatingCount);
Assert.Equal (4, activatingCount);
Assert.Equal (4, acceptingCount);
}
@@ -542,7 +542,7 @@ public class ButtonTests
injector.InjectMouse (new () { ScreenPosition = clickPos, Flags = MouseFlags.LeftButtonReleased, Timestamp = baseTime.AddMilliseconds (50) }, options);
// Assert - Button should receive Clicked event and fire both Activating and Accepting
Assert.Equal (2, activatingCount);
Assert.Equal (1, activatingCount);
Assert.Equal (1, acceptingCount);
// Act - Second click with timestamp spacing >500ms should be a new single click
@@ -551,7 +551,7 @@ public class ButtonTests
injector.InjectMouse (new () { ScreenPosition = clickPos, Flags = MouseFlags.LeftButtonReleased, Timestamp = baseTime.AddMilliseconds (650) }, options);
// Assert - Should fire again (two independent single clicks, not a double-click)
Assert.Equal (4, activatingCount);
Assert.Equal (2, activatingCount);
Assert.Equal (2, acceptingCount);
// Cleanup

View File

@@ -37,7 +37,7 @@ public class CheckBoxTests
Assert.Equal (CheckState.Checked, checkBox.Value);
// Select with mouse
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonPressed }));
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonReleased }));
Assert.Equal (CheckState.UnChecked, checkBox.Value);
checkBox.AllowCheckStateNone = true;
@@ -318,7 +318,7 @@ public class CheckBoxTests
}
[Fact]
public void LeftButtonPressed_Selects ()
public void LeftButtonReleased_Activates ()
{
var checkBox = new CheckBox { Text = "_Checkbox" };
Assert.True (checkBox.CanFocus);
@@ -345,14 +345,14 @@ public class CheckBoxTests
Assert.Equal (1, selectCount);
Assert.Equal (0, acceptCount);
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonPressed }));
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonReleased }));
Assert.Equal (CheckState.UnChecked, checkBox.Value);
Assert.Equal (2, checkedStateChangingCount);
Assert.Equal (2, selectCount);
Assert.Equal (0, acceptCount);
checkBox.AllowCheckStateNone = true;
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonPressed }));
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonReleased }));
Assert.Equal (CheckState.None, checkBox.Value);
Assert.Equal (3, checkedStateChangingCount);
Assert.Equal (3, selectCount);

View File

@@ -55,7 +55,7 @@ public class LabelTests (ITestOutputHelper output) : TestDriverBase
[Theory]
[CombinatorialData]
public void LeftButtonPressed_SetsFocus_OnNextSubView (bool hasHotKey)
public void LeftButtonReleased_SetsFocus_OnNextSubView (bool hasHotKey)
{
var superView = new View { CanFocus = true, Height = 1, Width = 15 };
var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
@@ -71,7 +71,7 @@ public class LabelTests (ITestOutputHelper output) : TestDriverBase
Assert.False (label.HasFocus);
Assert.False (nextSubView.HasFocus);
label.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonPressed });
label.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.LeftButtonReleased });
Assert.False (label.HasFocus);
Assert.Equal (hasHotKey, nextSubView.HasFocus);
}
@@ -180,7 +180,7 @@ public class LabelTests (ITestOutputHelper output) : TestDriverBase
}
[Fact]
public void CanFocus_False_LeftButtonPressed_SetsFocus_Next ()
public void CanFocus_False_LeftButtonReleased_SetsFocus_Next ()
{
View otherView = new ()
{
@@ -210,7 +210,7 @@ public class LabelTests (ITestOutputHelper output) : TestDriverBase
otherView.SetFocus ();
// click on label
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = label.Frame.Location, Flags = MouseFlags.LeftButtonPressed });
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = label.Frame.Location, Flags = MouseFlags.LeftButtonReleased });
Assert.False (label.HasFocus);
Assert.True (nextView.HasFocus);
}
@@ -240,7 +240,7 @@ public class LabelTests (ITestOutputHelper output) : TestDriverBase
}
[Fact]
public void CanFocus_True_LeftButtonPressed_Focuses ()
public void CanFocus_True_LeftButtonReleased_Focuses ()
{
Label label = new () { Text = "label", X = 0, Y = 0, CanFocus = true };
@@ -270,12 +270,12 @@ public class LabelTests (ITestOutputHelper output) : TestDriverBase
Assert.True (otherView.HasFocus);
// label can focus, so clicking on it set focus
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.LeftButtonPressed });
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.LeftButtonReleased });
Assert.True (label.HasFocus);
Assert.False (otherView.HasFocus);
// click on view
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 1), Flags = MouseFlags.LeftButtonPressed });
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (0, 1), Flags = MouseFlags.LeftButtonReleased });
Assert.False (label.HasFocus);
Assert.True (otherView.HasFocus);
}