Skip to content

Commit

Permalink
Fixed cancel logic. Title now shows for non-modal. (#1871)
Browse files Browse the repository at this point in the history
  • Loading branch information
tig authored Jul 21, 2022
1 parent 0af92ae commit e4a919f
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 32 deletions.
85 changes: 57 additions & 28 deletions Terminal.Gui/Windows/Wizard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,33 @@

namespace Terminal.Gui {
/// <summary>
/// Provides a step-based "wizard" UI. The Wizard supports multiple steps. Each step (<see cref="WizardStep"/>) can host
/// Provides navigation and a user interface (UI) to collect related data across multiple steps. Each step (<see cref="WizardStep"/>) can host
/// arbitrary <see cref="View"/>s, much like a <see cref="Dialog"/>. Each step also has a pane for help text. Along the
/// bottom of the Wizard view are customizable buttons enabling the user to navigate forward and backward through the Wizard.
/// </summary>
/// <remarks>
/// The Wizard can be shown either as a modal pop-up (the default) or embedded in a containing <see cref="View"/>. To use a a <see cref="View"/>,
/// set <see cref="Toplevel.Modal"/> to `false`.
/// The Wizard can be displayed either as a modal (pop-up) <see cref="Window"/> (like <see cref="Dialog"/>) or as an embedded <see cref="View"/>.
///
/// By default, <see cref="Modal"/> is true; launch the Wizard using `Application.Run(wizard)`.
///
/// Set <see cref="Modal"/> to `false` to use Wizard as an embedded View, and add the Wizard to a containing view with <see cref="View.Add(View)"/>.
///
/// When used as a modal pop-up window, the Esc key will cause the <see cref="Cancelled"/> event to fire and (if the event is not cancelled),
/// will cause <see cref="Application.RequestStop(Toplevel)"/> to be called, closing the Wizard.
///
/// When used as an embedded View, no frame is drawn around the Wizard. To detect if the user wants to cancel
/// the Wizard, subscrie to the <see cref="Cancelled"/> event.
///
/// </remarks>
public class Wizard : Dialog {

/// <summary>
/// One step for the Wizard. The <see cref="WizardStep"/> view is divided horizontally in two. On the left is the
/// Represents a basic step that is displayed in a <see cref="Wizard"/>. The <see cref="WizardStep"/> view is divided horizontally in two. On the left is the
/// content view where <see cref="View"/>s can be added, On the right is the help for the step.
/// Set <see cref="WizardStep.HelpText"/> to set the help text. If the help text is empty the help pane will not
/// be shown.
/// If there are no Views added to the WizardStep, and the help text is not empty the help text will
/// fill the wizard step.
///
/// If there are no Views added to the WizardStep the <see cref="HelpText"/> (if not empty) will fill the wizard step.
/// </summary>
/// <remarks>
/// If <see cref="Button"/>s are added, do not set <see cref="Button.IsDefault"/> to true as this will conflict
Expand All @@ -35,8 +45,9 @@ public class Wizard : Dialog {
/// </remarks>
public class WizardStep : FrameView {
/// <summary>
/// The title of the <see cref="WizardStep"/>.
/// The title of the <see cref="WizardStep"/>.
/// </summary>
/// <remarks>The Title is only displayed when the <see cref="Wizard"/> is used as a modal pop-up.</remarks>
public new ustring Title {
get => title;
set {
Expand All @@ -45,6 +56,7 @@ public class WizardStep : FrameView {
title = value;
OnTitleChanged (old, title);
}
base.Title = value;
SetNeedsDisplay ();
}
}
Expand Down Expand Up @@ -81,6 +93,7 @@ public TitleEventArgs (ustring oldTitle, ustring newTitle)
NewTitle = newTitle;
}
}

/// <summary>
/// Called before the <see cref="Title"/> changes. Invokes the <see cref="TitleChanging"/> event, which can be cancelled.
/// </summary>
Expand Down Expand Up @@ -116,18 +129,17 @@ public virtual void OnTitleChanged (ustring oldTitle, ustring newTitle)
/// </summary>
public event Action<TitleEventArgs> TitleChanged;

// The controlPane is a separate view, so when devs add controls to the Step and help is visible, Y = Pos.AnchorEnd()
// will work as expected.
// The contentView works like the ContentView in FrameView.
private View contentView = new View ();

/// <summary>
/// Sets or gets help text for the <see cref="WizardStep"/>.If <see cref="WizardStep.HelpText"/> is empty
/// the help pane will not be visible and the content will fill the entire WizardStep.
/// </summary>
/// <remarks>The help text is displayed using a read-only <see cref="TextView"/>.</remarks>
public ustring HelpText {
get => helpTextView.Text;
set {
public ustring HelpText {
get => helpTextView.Text;
set {
helpTextView.Text = value;
ShowHide ();
SetNeedsDisplay ();
Expand All @@ -141,14 +153,12 @@ public ustring HelpText {
/// </summary>
/// <remarks>The default text is "Back"</remarks>
public ustring BackButtonText { get; set; } = ustring.Empty;
// TODO: Update button text of Wizard button when step's button text is changed if step is current - this will require step to slueth it's parent

/// <summary>
/// Sets or gets the text for the next/finish button.
/// </summary>
/// <remarks>The default text is "Next..." if the Pane is not the last pane. Otherwise it is "Finish"</remarks>
public ustring NextButtonText { get; set; } = ustring.Empty;
// TODO: Update button text of Wizard button when step's button text is changed if step is current - this will require step to slueth it's parent

/// <summary>
/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
Expand Down Expand Up @@ -290,7 +300,7 @@ public override void RemoveAll ()
ShowHide ();
}

} // WizardStep
} // end of WizardStep class

/// <summary>
/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
Expand All @@ -306,7 +316,7 @@ public Wizard () : this (ustring.Empty)
/// <summary>
/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
/// </summary>
/// <param name="title">Title for the Wizard.</param>
/// <param name="title">Sets the <see cref="Title"/> for the Wizard.</param>
/// <remarks>
/// The Wizard will be vertically and horizontally centered in the container.
/// After initialization use <c>X</c>, <c>Y</c>, <c>Width</c>, and <c>Height</c> change size and position.
Expand Down Expand Up @@ -339,6 +349,12 @@ public Wizard (ustring title) : base (title)

Loaded += Wizard_Loaded;
Closing += Wizard_Closing;

if (Modal) {
ClearKeybinding (Command.QuitToplevel);
AddKeyBinding (Key.Esc, Command.QuitToplevel);
}

}

private void Wizard_Loaded ()
Expand Down Expand Up @@ -382,16 +398,19 @@ private void NextfinishBtn_Clicked ()
///<inheritdoc/>
public override bool ProcessKey (KeyEvent kb)
{
switch (kb.Key) {
case Key.Esc:
// Dialog causes ESC to close/cancel; we dont want that with wizard
// Use QuitKey instead.
return false;
if (!Modal) {
switch (kb.Key) {
case Key.Esc:
// Dialog causes ESC to RequestStop; we dont want that with a non-modal wizard
// Instead, we fire the Cancelled event.
var args = new WizardButtonEventArgs ();
Cancelled?.Invoke (args);
return false;
}
}
return base.ProcessKey (kb);
}


/// <summary>
/// Causes the wizad to move to the next enabled step (or last step if <see cref="CurrentStep"/> is not set).
/// If there is no previous step, does nothing.
Expand Down Expand Up @@ -548,6 +567,7 @@ public void AddStep (WizardStep newStep)
/// <summary>
/// The title of the Wizard, shown at the top of the Wizard with " - currentStep.Title" appended.
/// </summary>
/// <remarks>The Title is only displayed when the <see cref="Wizard"/> is used as a modal pop-up.</remarks>
public new ustring Title {
get {
// The base (Dialog) Title holds the full title ("Wizard Title - Step Title")
Expand Down Expand Up @@ -585,9 +605,9 @@ public WizardButtonEventArgs ()
public event Action<WizardButtonEventArgs> MovingBack;

/// <summary>
/// This event is raised when the Next/Finish button in the <see cref="Wizard"/> is clicked. The Next/Finish button is always
/// the last button in the array of Buttons passed to the <see cref="Wizard"/> constructor, if any. This event is only
/// raised if the <see cref="CurrentStep"/> is the last Step in the Wizard flow
/// This event is raised when the Next/Finish button in the <see cref="Wizard"/> is clicked (or the user presses Enter).
/// The Next/Finish button is always the last button in the array of Buttons passed to the <see cref="Wizard"/> constructor,
/// if any. This event is only raised if the <see cref="CurrentStep"/> is the last Step in the Wizard flow
/// (otherwise the <see cref="Finished"/> event is raised).
/// </summary>
public event Action<WizardButtonEventArgs> MovingNext;
Expand All @@ -600,9 +620,10 @@ public WizardButtonEventArgs ()
/// </summary>
public event Action<WizardButtonEventArgs> Finished;


/// <summary>
/// This event is raised when the user has cancelled the <see cref="Wizard"/> (with Ctrl-Q or ESC).
/// This event is raised when the user has cancelled the <see cref="Wizard"/> by pressin the Esc key.
/// To prevent a <see cref="Modal"/> Wizard from
/// closing, cancel the event by setting <see cref="WizardButtonEventArgs.Cancel"/> to `true` before returning from the event handler.
/// </summary>
public event Action<WizardButtonEventArgs> Cancelled;

Expand Down Expand Up @@ -762,7 +783,15 @@ private void SizeStep (WizardStep step)
}
}

/// <inheritdoc/>
/// <summary>
/// Determines whether the <see cref="Wizard"/> is displayed as modal pop-up or not.
///
/// The default is `true`. The Wizard will be shown with a frame with <see cref="Title"/> and will behave like
/// any <see cref="Toplevel"/> window.
///
/// If set to `false` the Wizard will have no frame and will behave like any embedded <see cref="View"/>.
///
/// </summary>
public new bool Modal {
get => base.Modal;
set {
Expand Down
12 changes: 10 additions & 2 deletions UICatalog/Scenarios/WizardAsView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace UICatalog.Scenarios {
[ScenarioMetadata (Name: "WizardAsView", Description: "Shows using the Wizard class in an non-modal way")]
[ScenarioCategory ("Dialogs")]
[ScenarioCategory ("Wizards")]
public class WizardAsView : Scenario {

public override void Init (Toplevel top, ColorScheme colorScheme)
Expand Down Expand Up @@ -51,6 +51,14 @@ public override void Init (Toplevel top, ColorScheme colorScheme)
Application.RequestStop ();
};

wizard.Cancelled += (args) => {
var btn = MessageBox.Query ("Setup Wizard", "Are you sure you want to cancel?", "Yes", "No");
args.Cancel = btn == 1;
if (btn == 0) {
Application.RequestStop ();
}
};

// Add 1st step
var firstStep = new Wizard.WizardStep ("End User License Agreement");
wizard.AddStep (firstStep);
Expand Down Expand Up @@ -83,7 +91,7 @@ public override void Init (Toplevel top, ColorScheme colorScheme)
// Add last step
var lastStep = new Wizard.WizardStep ("The last step");
wizard.AddStep (lastStep);
lastStep.HelpText = "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing ESC will cancel the wizard.";
lastStep.HelpText = "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing Esc will cancel.";

// When run as a modal, Wizard gets a Loading event where it sets the
// Current Step. But when running non-modal it must be done manually.
Expand Down
4 changes: 2 additions & 2 deletions UICatalog/Scenarios/Wizards.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace UICatalog.Scenarios {
[ScenarioMetadata (Name: "Wizards", Description: "Demonstrates the Wizard class")]
[ScenarioCategory ("Dialogs")]
[ScenarioCategory ("Dialogs"), ScenarioCategory ("Top Level Windows"), ScenarioCategory ("Wizards")]
public class Wizards : Scenario {
public override void Setup ()
{
Expand Down Expand Up @@ -65,7 +65,7 @@ public override void Setup ()
AutoSize = false
};
frame.Add (label);
var titleEdit = new TextField ("Title") {
var titleEdit = new TextField ("Gandolf") {
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Width = Dim.Fill (),
Expand Down

0 comments on commit e4a919f

Please sign in to comment.