Skip to content

Commit 7ba6d63

Browse files
authoredFeb 26, 2025
Fixes gui-cs#3918 and gui-cs#3913 - Accepting behavior (gui-cs#3921)
* Fixed gui-cs#3905, gui-cs#3918 * Tweaked Generic * Label code cleanup * Clean up. * Clean up. * Clean up2.
1 parent 35522cc commit 7ba6d63

File tree

13 files changed

+194
-23
lines changed

13 files changed

+194
-23
lines changed
 

‎CommunityToolkitExample/LoginView.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,19 @@ public LoginView (LoginViewModel viewModel)
1919
{
2020
ViewModel.Password = passwordInput.Text;
2121
};
22-
loginButton.Accepting += (_, _) =>
22+
loginButton.Accepting += (_, e) =>
2323
{
2424
if (!ViewModel.CanLogin) { return; }
2525
ViewModel.LoginCommand.Execute (null);
26+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
27+
e.Cancel = false;
2628
};
2729

28-
clearButton.Accepting += (_, _) =>
30+
clearButton.Accepting += (_, e) =>
2931
{
3032
ViewModel.ClearCommand.Execute (null);
33+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
34+
e.Cancel = false;
3135
};
3236

3337
Initialized += (_, _) => { ViewModel.Initialized (); };

‎Example/Example.cs

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public ExampleWindow ()
7878
{
7979
MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
8080
}
81+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
82+
e.Cancel = false;
8183
};
8284

8385
// Add the views to the Window

‎NativeAot/Program.cs

+2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ public ExampleWindow ()
105105
{
106106
MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
107107
}
108+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
109+
e.Cancel = false;
108110
};
109111

110112
// Add the views to the Window

‎SelfContained/Program.cs

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ public ExampleWindow ()
104104
{
105105
MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
106106
}
107+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
108+
e.Cancel = false;
107109
};
108110

109111
// Add the views to the Window

‎Terminal.Gui/FileServices/DefaultFileOperations.cs

+4
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,17 @@ private bool Prompt (string title, string defaultText, out string result)
139139
{
140140
confirm = true;
141141
Application.RequestStop ();
142+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
143+
e.Cancel = false;
142144
};
143145
var btnCancel = new Button { Text = Strings.btnCancel };
144146

145147
btnCancel.Accepting += (s, e) =>
146148
{
147149
confirm = false;
148150
Application.RequestStop ();
151+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
152+
e.Cancel = false;
149153
};
150154

151155
var lbl = new Label { Text = Strings.fdRenamePrompt };

‎Terminal.Gui/View/View.Mouse.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,8 @@ internal bool WhenGrabbedHandleClicked (MouseEventArgs mouseEvent)
559559
// If mouse is still in bounds, generate a click
560560
if (!WantMousePositionReports && Viewport.Contains (mouseEvent.Position))
561561
{
562-
return RaiseMouseClickEvent (mouseEvent);
562+
563+
return RaiseMouseClickEvent (mouseEvent);
563564
}
564565

565566
return mouseEvent.Handled = true;

‎Terminal.Gui/Views/Button.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,10 @@ public override Rune HotKeySpecifier
165165
/// If <see langword="true"/>:
166166
/// </para>
167167
/// <para>
168-
/// - the Button will display an indicator that it is the default Button.
168+
/// - The Button will display an indicator that it is the default Button.
169169
/// </para>
170170
/// <para>
171-
/// - when clicked, if the Accepting event is not handled, <see cref="Command.Accept"/> will be
171+
/// - When clicked, if the Accepting event is not handled, <see cref="Command.Accept"/> will be
172172
/// invoked on the SuperView.
173173
/// </para>
174174
/// <para>
@@ -197,7 +197,7 @@ public bool IsDefault
197197

198198
/// <summary>
199199
/// Gets or sets whether the Button will show decorations or not. If <see langword="true"/> the glyphs that normally
200-
/// brakcet the Button Title and the <see cref="IsDefault"/> indicator will not be shown.
200+
/// bracket the Button Title and the <see cref="IsDefault"/> indicator will not be shown.
201201
/// </summary>
202202
public bool NoDecorations { get; set; }
203203

‎Terminal.Gui/Views/Label.cs

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/// <summary>
44
/// The Label <see cref="View"/> displays text that describes the View next in the <see cref="View.Subviews"/>. When
55
/// Label
6-
/// recieves a <see cref="Command.HotKey"/> command it will pass it to the next <see cref="View"/> in
6+
/// receives a <see cref="Command.HotKey"/> command it will pass it to the next <see cref="View"/> in
77
/// <see cref="View.Subviews"/>.
88
/// </summary>
99
/// <remarks>
@@ -13,7 +13,7 @@
1313
/// <para>
1414
/// If <see cref="View.CanFocus"/> is <see langword="false"/> and the use clicks on the Label,
1515
/// the <see cref="Command.HotKey"/> will be invoked on the next <see cref="View"/> in
16-
/// <see cref="View.Subviews"/>."
16+
/// <see cref="View.Subviews"/>.
1717
/// </para>
1818
/// </remarks>
1919
public class Label : View, IDesignable
@@ -31,7 +31,6 @@ public Label ()
3131
MouseClick += Label_MouseClick;
3232
}
3333

34-
// TODO: base raises Select, but we want to raise HotKey. This can be simplified?
3534
private void Label_MouseClick (object sender, MouseEventArgs e)
3635
{
3736
if (!CanFocus)
@@ -74,12 +73,15 @@ public override Rune HotKeySpecifier
7473
return true;
7574
}
7675

77-
int me = SuperView?.Subviews.IndexOf (this) ?? -1;
78-
79-
if (me != -1 && me < SuperView?.Subviews.Count - 1)
76+
if (HotKey.IsValid)
8077
{
78+
int me = SuperView?.Subviews.IndexOf (this) ?? -1;
79+
80+
if (me != -1 && me < SuperView?.Subviews.Count - 1)
81+
{
8182

82-
return SuperView?.Subviews [me + 1].InvokeCommand (Command.HotKey) == true;
83+
return SuperView?.Subviews [me + 1].InvokeCommand (Command.HotKey) == true;
84+
}
8385
}
8486

8587
return false;

‎UICatalog/Scenarios/Generic.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ public override void Main ()
2020

2121
var button = new Button { Id = "button", X = Pos.Center (), Y = 1, Text = "_Press me!" };
2222

23-
button.Accepting += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
23+
button.Accepting += (s, e) =>
24+
{
25+
// Anytime Accepting is handled, make sure to set e.Cancel to false.
26+
e.Cancel = true;
27+
MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
28+
};
29+
2430
appWindow.Add (button);
2531

2632
// Run - Start the application.

‎UnitTests/View/ViewCommandTests.cs

+144-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public void Accept_Command_Raises_NoFocus ()
1010
var view = new ViewEventTester ();
1111
Assert.False (view.HasFocus);
1212

13-
Assert.False (view.InvokeCommand (Command.Accept)); // false means it was not handled
13+
Assert.False (view.InvokeCommand (Command.Accept)); // there's no superview, so it should return true?
1414

1515
Assert.Equal (1, view.OnAcceptedCount);
1616

@@ -124,6 +124,148 @@ public void MouseClick_Does_Not_Invoke_Accept_Command ()
124124
Assert.Equal (0, view.OnAcceptedCount);
125125
}
126126

127+
// See https://github.com/gui-cs/Terminal.Gui/issues/3913
128+
[Fact]
129+
public void Button_IsDefault_Raises_Accepted_Correctly ()
130+
{
131+
int A_AcceptedCount = 0;
132+
bool A_CancelAccepting = false;
133+
134+
int B_AcceptedCount = 0;
135+
bool B_CancelAccepting = false;
136+
137+
var w = new Window ()
138+
{
139+
BorderStyle = LineStyle.None,
140+
Width = 10,
141+
Height = 10
142+
};
143+
144+
var btnA = new Button ()
145+
{
146+
Width = 3,
147+
IsDefault = true
148+
};
149+
btnA.Accepting += (s, e) =>
150+
{
151+
A_AcceptedCount++;
152+
e.Cancel = A_CancelAccepting;
153+
};
154+
155+
var btnB = new Button ()
156+
{
157+
Width = 3,
158+
X = Pos.Right (btnA)
159+
};
160+
161+
btnB.Accepting += (s, e) =>
162+
{
163+
B_AcceptedCount++;
164+
e.Cancel = B_CancelAccepting;
165+
};
166+
w.Add (btnA, btnB);
167+
168+
w.LayoutSubviews ();
169+
170+
Application.Begin (w);
171+
Assert.Same (Application.Top, w);
172+
173+
// Click button 2
174+
var btn2Frame = btnB.FrameToScreen ();
175+
176+
Application.RaiseMouseEvent (
177+
new MouseEventArgs ()
178+
{
179+
ScreenPosition = btn2Frame.Location,
180+
Flags = MouseFlags.Button1Clicked
181+
});
182+
183+
// Button A should have been accepted because B didn't cancel and A IsDefault
184+
Assert.Equal (1, A_AcceptedCount);
185+
Assert.Equal (1, B_AcceptedCount);
186+
187+
B_CancelAccepting = true;
188+
Application.RaiseMouseEvent (
189+
new MouseEventArgs ()
190+
{
191+
ScreenPosition = btn2Frame.Location,
192+
Flags = MouseFlags.Button1Clicked
193+
});
194+
195+
// Button A (IsDefault) should NOT have been accepted because B canceled
196+
Assert.Equal (1, A_AcceptedCount);
197+
Assert.Equal (2, B_AcceptedCount);
198+
}
199+
200+
// See: https://github.com/gui-cs/Terminal.Gui/issues/3905
201+
[Fact]
202+
public void Button_CanFocus_False_Raises_Accepted_Correctly ()
203+
{
204+
int wAcceptedCount = 0;
205+
bool wCancelAccepting = false;
206+
var w = new Window ()
207+
{
208+
Title = "Window",
209+
BorderStyle = LineStyle.None,
210+
Width = 10,
211+
Height = 10
212+
};
213+
214+
w.Accepting += (s, e) =>
215+
{
216+
wAcceptedCount++;
217+
e.Cancel = wCancelAccepting;
218+
};
219+
220+
int btnAcceptedCount = 0;
221+
bool btnCancelAccepting = false;
222+
var btn = new Button ()
223+
{
224+
Title = "Button",
225+
Width = 3,
226+
IsDefault = true,
227+
};
228+
btn.CanFocus = true;
229+
230+
btn.Accepting += (s, e) =>
231+
{
232+
btnAcceptedCount++;
233+
e.Cancel = btnCancelAccepting;
234+
};
235+
236+
w.Add (btn);
237+
238+
w.LayoutSubviews ();
239+
240+
Application.Begin (w);
241+
242+
// Click button just like a driver would
243+
var btnFrame = btn.FrameToScreen ();
244+
Application.RaiseMouseEvent (
245+
new MouseEventArgs ()
246+
{
247+
ScreenPosition = btnFrame.Location,
248+
Flags = MouseFlags.Button1Pressed
249+
});
250+
251+
Application.RaiseMouseEvent (
252+
new MouseEventArgs ()
253+
{
254+
ScreenPosition = btnFrame.Location,
255+
Flags = MouseFlags.Button1Released
256+
});
257+
258+
Application.RaiseMouseEvent (
259+
new MouseEventArgs ()
260+
{
261+
ScreenPosition = btnFrame.Location,
262+
Flags = MouseFlags.Button1Clicked
263+
});
264+
265+
Assert.Equal (1, btnAcceptedCount);
266+
Assert.Equal (2, wAcceptedCount);
267+
}
268+
127269
#endregion OnAccept/Accept tests
128270

129271
#region OnSelect/Select tests
@@ -140,7 +282,7 @@ public void Select_Command_Raises_SetsFocus (bool canFocus)
140282
Assert.Equal (canFocus, view.CanFocus);
141283
Assert.False (view.HasFocus);
142284

143-
Assert.Equal (canFocus, view.InvokeCommand (Command.Select));
285+
view.InvokeCommand (Command.Select);
144286

145287
Assert.Equal (1, view.OnSelectingCount);
146288

‎UnitTests/Views/LabelTests.cs

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.ComponentModel;
2+
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
23
using Xunit.Abstractions;
34

45
namespace Terminal.Gui.ViewsTests;
@@ -30,11 +31,13 @@ public void Title_Mirrors_Text ()
3031
Assert.Equal ("Hello", label.TitleTextFormatter.Text);
3132
}
3233

33-
[Fact]
34-
public void HotKey_Command_SetsFocus_OnNextSubview ()
34+
[Theory]
35+
[CombinatorialData]
36+
public void HotKey_Command_SetsFocus_OnNextSubview (bool hasHotKey)
3537
{
3638
var superView = new View { CanFocus = true };
3739
var label = new Label ();
40+
label.HotKey = hasHotKey ? Key.A.WithAlt : Key.Empty;
3841
var nextSubview = new View { CanFocus = true };
3942
superView.Add (label, nextSubview);
4043
superView.BeginInit ();
@@ -45,15 +48,18 @@ public void HotKey_Command_SetsFocus_OnNextSubview ()
4548

4649
label.InvokeCommand (Command.HotKey);
4750
Assert.False (label.HasFocus);
48-
Assert.True (nextSubview.HasFocus);
51+
Assert.Equal (hasHotKey, nextSubview.HasFocus);
4952
}
5053

51-
[Fact]
52-
public void MouseClick_SetsFocus_OnNextSubview ()
54+
[Theory]
55+
[CombinatorialData]
56+
public void MouseClick_SetsFocus_OnNextSubview (bool hasHotKey)
5357
{
5458
var superView = new View { CanFocus = true, Height = 1, Width = 15 };
5559
var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
56-
var label = new Label { X = 2, Title = "_x" };
60+
var label = new Label { X = 2 };
61+
label.HotKey = hasHotKey ? Key.X.WithAlt : Key.Empty;
62+
5763
var nextSubview = new View { CanFocus = true, X = 4, Width = 4, Height = 1 };
5864
superView.Add (focusedView, label, nextSubview);
5965
superView.BeginInit ();
@@ -65,7 +71,7 @@ public void MouseClick_SetsFocus_OnNextSubview ()
6571

6672
label.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
6773
Assert.False (label.HasFocus);
68-
Assert.True (nextSubview.HasFocus);
74+
Assert.Equal (hasHotKey, nextSubview.HasFocus);
6975
}
7076

7177
[Fact]
30.2 KB
Binary file not shown.
7.28 KB
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.