Skip to content

Commit 7a8b6e4

Browse files
Copilottig
andauthored
Fixes #4167. Add Accepted event to View (#4452)
* Initial plan * Add Accepted event to View and remove duplicate implementations Co-authored-by: tig <[email protected]> * Update RaiseAccepting to call RaiseAccepted and add comprehensive tests Co-authored-by: tig <[email protected]> * Fix code style violations - use explicit types and target-typed new Co-authored-by: tig <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: tig <[email protected]> Co-authored-by: Tig <[email protected]>
1 parent 5e3175c commit 7a8b6e4

File tree

5 files changed

+166
-108
lines changed

5 files changed

+166
-108
lines changed

Terminal.Gui/ViewBase/View.Command.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ private void SetupCommands ()
141141
Accepting?.Invoke (this, args);
142142
}
143143

144+
// If Accepting was handled, raise Accepted (non-cancelable event)
145+
if (args.Handled)
146+
{
147+
Logging.Debug ($"{Title} ({ctx?.Source?.Title}) - Calling RaiseAccepted");
148+
RaiseAccepted (ctx);
149+
}
150+
144151
// Accept is a special case where if the event is not canceled, the event is
145152
// - Invoked on any peer-View with IsDefault == true
146153
// - bubbled up the SuperView hierarchy.
@@ -201,6 +208,48 @@ private void SetupCommands ()
201208
/// </remarks>
202209
public event EventHandler<CommandEventArgs>? Accepting;
203210

211+
/// <summary>
212+
/// Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating the View has been accepted.
213+
/// This is called after <see cref="Accepting"/> has been raised and not cancelled.
214+
/// </summary>
215+
/// <remarks>
216+
/// <para>
217+
/// Unlike <see cref="Accepting"/>, this event cannot be cancelled. It is raised after the View has been accepted.
218+
/// </para>
219+
/// </remarks>
220+
/// <param name="ctx">The command context.</param>
221+
protected void RaiseAccepted (ICommandContext? ctx)
222+
{
223+
CommandEventArgs args = new () { Context = ctx };
224+
225+
OnAccepted (args);
226+
Accepted?.Invoke (this, args);
227+
}
228+
229+
/// <summary>
230+
/// Called when the View has been accepted. This is called after <see cref="Accepting"/> has been raised and not cancelled.
231+
/// </summary>
232+
/// <remarks>
233+
/// <para>
234+
/// Unlike <see cref="OnAccepting"/>, this method is called after the View has been accepted and cannot cancel the operation.
235+
/// </para>
236+
/// </remarks>
237+
/// <param name="args">The event arguments.</param>
238+
protected virtual void OnAccepted (CommandEventArgs args) { }
239+
240+
/// <summary>
241+
/// Event raised when the View has been accepted. This is raised after <see cref="Accepting"/> has been raised and not cancelled.
242+
/// </summary>
243+
/// <remarks>
244+
/// <para>
245+
/// Unlike <see cref="Accepting"/>, this event cannot be cancelled. It is raised after the View has been accepted.
246+
/// </para>
247+
/// <para>
248+
/// See <see cref="RaiseAccepted"/> for more information.
249+
/// </para>
250+
/// </remarks>
251+
public event EventHandler<CommandEventArgs>? Accepted;
252+
204253
/// <summary>
205254
/// Called when the user has performed an action (e.g. <see cref="Command.Select"/>) causing the View to change state.
206255
/// Calls <see cref="OnSelecting"/> which can be cancelled; if not cancelled raises <see cref="Accepting"/>.

Terminal.Gui/Views/Menu/Menu.cs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -136,40 +136,7 @@ protected override bool OnAccepting (CommandEventArgs args)
136136
return false;
137137
}
138138

139-
// TODO: Consider moving Accepted to Bar?
140139

141-
/// <summary>
142-
/// Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating an item in this menu (or submenu)
143-
/// was accepted. This is used to determine when to hide the menu.
144-
/// </summary>
145-
/// <param name="ctx"></param>
146-
/// <returns></returns>
147-
protected void RaiseAccepted (ICommandContext? ctx)
148-
{
149-
//Logging.Trace ($"RaiseAccepted: {ctx}");
150-
CommandEventArgs args = new () { Context = ctx };
151-
152-
OnAccepted (args);
153-
Accepted?.Invoke (this, args);
154-
}
155-
156-
/// <summary>
157-
/// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu.
158-
/// </summary>
159-
/// <remarks>
160-
/// </remarks>
161-
/// <param name="args"></param>
162-
protected virtual void OnAccepted (CommandEventArgs args) { }
163-
164-
/// <summary>
165-
/// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu.
166-
/// </summary>
167-
/// <remarks>
168-
/// <para>
169-
/// See <see cref="RaiseAccepted"/> for more information.
170-
/// </para>
171-
/// </remarks>
172-
public event EventHandler<CommandEventArgs>? Accepted;
173140

174141
/// <inheritdoc />
175142
protected override void OnFocusedChanged (View? previousFocused, View? focused)

Terminal.Gui/Views/Menu/MenuItem.cs

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,10 @@ public Command Command
143143
{
144144
// Logging.Debug ($"{Title} - calling base.DispatchCommand...");
145145
// Base will Raise Selected, then Accepting, then invoke the Action, if any
146+
// Note: base.DispatchCommand will call RaiseAccepted via RaiseAccepting when handled
146147
ret = base.DispatchCommand (commandContext);
147148
}
148149

149-
if (ret is true)
150-
{
151-
// Logging.Debug ($"{Title} - Calling RaiseAccepted");
152-
RaiseAccepted (commandContext);
153-
}
154-
155150
return ret;
156151
}
157152

@@ -205,42 +200,7 @@ protected override bool OnMouseEnter (CancelEventArgs eventArgs)
205200
return base.OnMouseEnter (eventArgs);
206201
}
207202

208-
// TODO: Consider moving Accepted to Shortcut?
209-
210-
/// <summary>
211-
/// Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating this item (or submenu)
212-
/// was accepted. This is used to determine when to hide the menu.
213-
/// </summary>
214-
/// <param name="ctx"></param>
215-
/// <returns></returns>
216-
protected void RaiseAccepted (ICommandContext? ctx)
217-
{
218-
//Logging.Trace ($"RaiseAccepted: {ctx}");
219-
CommandEventArgs args = new () { Context = ctx };
220-
221-
OnAccepted (args);
222-
Accepted?.Invoke (this, args);
223-
}
224-
225-
/// <summary>
226-
/// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
227-
/// menu.
228-
/// </summary>
229-
/// <remarks>
230-
/// </remarks>
231-
/// <param name="args"></param>
232-
protected virtual void OnAccepted (CommandEventArgs args) { }
233203

234-
/// <summary>
235-
/// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
236-
/// menu.
237-
/// </summary>
238-
/// <remarks>
239-
/// <para>
240-
/// See <see cref="RaiseAccepted"/> for more information.
241-
/// </para>
242-
/// </remarks>
243-
public event EventHandler<CommandEventArgs>? Accepted;
244204

245205
/// <inheritdoc/>
246206
protected override void Dispose (bool disposing)

Terminal.Gui/Views/Menu/PopoverMenu.cs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -560,40 +560,7 @@ protected override bool OnAccepting (CommandEventArgs args)
560560
return false;
561561
}
562562

563-
/// <summary>
564-
/// Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating a menu (or submenu)
565-
/// was accepted and the Menus in the PopoverMenu were hidden. Use this to determine when to hide the PopoverMenu.
566-
/// </summary>
567-
/// <param name="ctx"></param>
568-
/// <returns></returns>
569-
protected void RaiseAccepted (ICommandContext? ctx)
570-
{
571-
// Logging.Debug ($"{Title} - RaiseAccepted: {ctx}");
572-
CommandEventArgs args = new () { Context = ctx };
573-
574-
OnAccepted (args);
575-
Accepted?.Invoke (this, args);
576-
}
577563

578-
/// <summary>
579-
/// Called when the user has accepted an item in this menu (or submenu. This is used to determine when to hide the
580-
/// menu.
581-
/// </summary>
582-
/// <remarks>
583-
/// </remarks>
584-
/// <param name="args"></param>
585-
protected virtual void OnAccepted (CommandEventArgs args) { }
586-
587-
/// <summary>
588-
/// Raised when the user has accepted an item in this menu (or submenu. This is used to determine when to hide the
589-
/// menu.
590-
/// </summary>
591-
/// <remarks>
592-
/// <para>
593-
/// See <see cref="RaiseAccepted"/> for more information.
594-
/// </para>
595-
/// </remarks>
596-
public event EventHandler<CommandEventArgs>? Accepted;
597564

598565
private void MenuOnSelectedMenuItemChanged (object? sender, MenuItem? e)
599566
{

Tests/UnitTestsParallelizable/ViewBase/ViewCommandTests.cs

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,124 @@ public void MouseClick_Does_Not_Invoke_Accept_Command ()
124124
Assert.Equal (0, view.OnAcceptedCount);
125125
}
126126

127-
128127
#endregion OnAccept/Accept tests
129128

129+
#region Accepted tests
130+
131+
[Fact]
132+
public void Accepted_Event_Is_Raised_After_Accepting_When_Handled ()
133+
{
134+
View view = new ();
135+
var acceptingInvoked = false;
136+
var acceptedInvoked = false;
137+
138+
view.Accepting += (sender, e) =>
139+
{
140+
acceptingInvoked = true;
141+
e.Handled = true;
142+
};
143+
144+
view.Accepted += (sender, e) =>
145+
{
146+
acceptedInvoked = true;
147+
Assert.True (acceptingInvoked); // Accepting should be raised first
148+
};
149+
150+
bool? ret = view.InvokeCommand (Command.Accept);
151+
Assert.True (ret);
152+
Assert.True (acceptingInvoked);
153+
Assert.True (acceptedInvoked);
154+
}
155+
156+
[Fact]
157+
public void Accepted_Event_Not_Raised_When_Accepting_Not_Handled ()
158+
{
159+
View view = new ();
160+
var acceptingInvoked = false;
161+
var acceptedInvoked = false;
162+
163+
view.Accepting += (sender, e) =>
164+
{
165+
acceptingInvoked = true;
166+
e.Handled = false;
167+
};
168+
169+
view.Accepted += (sender, e) =>
170+
{
171+
acceptedInvoked = true;
172+
};
173+
174+
// When not handled, Accept bubbles to SuperView, so returns false (no superview)
175+
bool? ret = view.InvokeCommand (Command.Accept);
176+
Assert.False (ret);
177+
Assert.True (acceptingInvoked);
178+
Assert.False (acceptedInvoked); // Should not be invoked when not handled
179+
}
180+
181+
[Fact]
182+
public void Accepted_Event_Cannot_Be_Cancelled ()
183+
{
184+
View view = new ();
185+
var acceptedInvoked = false;
186+
187+
view.Accepting += (sender, e) =>
188+
{
189+
e.Handled = true;
190+
};
191+
192+
view.Accepted += (sender, e) =>
193+
{
194+
acceptedInvoked = true;
195+
// Accepted event has Handled property but it doesn't affect flow
196+
e.Handled = false;
197+
};
198+
199+
bool? ret = view.InvokeCommand (Command.Accept);
200+
Assert.True (ret);
201+
Assert.True (acceptedInvoked);
202+
}
203+
204+
[Fact]
205+
public void OnAccepted_Called_When_Accepting_Handled ()
206+
{
207+
OnAcceptedTestView view = new ();
208+
209+
view.Accepting += (sender, e) =>
210+
{
211+
e.Handled = true;
212+
};
213+
214+
view.InvokeCommand (Command.Accept);
215+
Assert.Equal (1, view.OnAcceptedCallCount);
216+
}
217+
218+
[Fact]
219+
public void OnAccepted_Not_Called_When_Accepting_Not_Handled ()
220+
{
221+
OnAcceptedTestView view = new ();
222+
223+
view.Accepting += (sender, e) =>
224+
{
225+
e.Handled = false;
226+
};
227+
228+
view.InvokeCommand (Command.Accept);
229+
Assert.Equal (0, view.OnAcceptedCallCount);
230+
}
231+
232+
private class OnAcceptedTestView : View
233+
{
234+
public int OnAcceptedCallCount { get; private set; }
235+
236+
protected override void OnAccepted (CommandEventArgs args)
237+
{
238+
OnAcceptedCallCount++;
239+
base.OnAccepted (args);
240+
}
241+
}
242+
243+
#endregion Accepted tests
244+
130245
#region OnSelect/Select tests
131246

132247
[Theory]

0 commit comments

Comments
 (0)