Browse Source

Fixed cancel logic. Title now shows for non-modal. (#1871)

Tig Kindel 3 years ago
parent
commit
e4a919fb94

+ 57 - 28
Terminal.Gui/Windows/Wizard.cs

@@ -6,23 +6,33 @@ using Terminal.Gui.Resources;
 
 
 namespace Terminal.Gui {
 namespace Terminal.Gui {
 	/// <summary>
 	/// <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
 	/// 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. 
 	/// bottom of the Wizard view are customizable buttons enabling the user to navigate forward and backward through the Wizard. 
 	/// </summary>
 	/// </summary>
 	/// <remarks>
 	/// <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>
 	/// </remarks>
 	public class Wizard : Dialog {
 	public class Wizard : Dialog {
 
 
 		/// <summary>
 		/// <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.
 		/// 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
 		/// Set <see cref="WizardStep.HelpText"/> to set the help text. If the help text is empty the help pane will not
 		/// be shown. 
 		/// 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>
 		/// </summary>
 		/// <remarks>
 		/// <remarks>
 		/// If <see cref="Button"/>s are added, do not set <see cref="Button.IsDefault"/> to true as this will conflict
 		/// If <see cref="Button"/>s are added, do not set <see cref="Button.IsDefault"/> to true as this will conflict
@@ -35,8 +45,9 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// </remarks>
 		public class WizardStep : FrameView {
 		public class WizardStep : FrameView {
 			/// <summary>
 			/// <summary>
-			/// The title of the <see cref="WizardStep"/>.
+			/// The title of the <see cref="WizardStep"/>. 
 			/// </summary>
 			/// </summary>
+			/// <remarks>The Title is only displayed when the <see cref="Wizard"/> is used as a modal pop-up.</remarks>
 			public new ustring Title {
 			public new ustring Title {
 				get => title;
 				get => title;
 				set {
 				set {
@@ -45,6 +56,7 @@ namespace Terminal.Gui {
 						title = value;
 						title = value;
 						OnTitleChanged (old, title);
 						OnTitleChanged (old, title);
 					}
 					}
+					base.Title = value;
 					SetNeedsDisplay ();
 					SetNeedsDisplay ();
 				}
 				}
 			}
 			}
@@ -81,6 +93,7 @@ namespace Terminal.Gui {
 					NewTitle = newTitle;
 					NewTitle = newTitle;
 				}
 				}
 			}
 			}
+
 			/// <summary>
 			/// <summary>
 			/// Called before the <see cref="Title"/> changes. Invokes the <see cref="TitleChanging"/> event, which can be cancelled.
 			/// Called before the <see cref="Title"/> changes. Invokes the <see cref="TitleChanging"/> event, which can be cancelled.
 			/// </summary>
 			/// </summary>
@@ -116,8 +129,7 @@ namespace Terminal.Gui {
 			/// </summary>
 			/// </summary>
 			public event Action<TitleEventArgs> TitleChanged;
 			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 ();
 			private View contentView = new View ();
 
 
 			/// <summary>
 			/// <summary>
@@ -125,9 +137,9 @@ namespace Terminal.Gui {
 			/// the help pane will not be visible and the content will fill the entire WizardStep.
 			/// the help pane will not be visible and the content will fill the entire WizardStep.
 			/// </summary>
 			/// </summary>
 			/// <remarks>The help text is displayed using a read-only <see cref="TextView"/>.</remarks>
 			/// <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;
 					helpTextView.Text = value;
 					ShowHide ();
 					ShowHide ();
 					SetNeedsDisplay ();
 					SetNeedsDisplay ();
@@ -141,14 +153,12 @@ namespace Terminal.Gui {
 			/// </summary>
 			/// </summary>
 			/// <remarks>The default text is "Back"</remarks>
 			/// <remarks>The default text is "Back"</remarks>
 			public ustring BackButtonText { get; set; } = ustring.Empty;
 			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>
 			/// <summary>
 			/// Sets or gets the text for the next/finish button.
 			/// Sets or gets the text for the next/finish button.
 			/// </summary>
 			/// </summary>
 			/// <remarks>The default text is "Next..." if the Pane is not the last pane. Otherwise it is "Finish"</remarks>
 			/// <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;
 			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>
 			/// <summary>
 			/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 			/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
@@ -290,7 +300,7 @@ namespace Terminal.Gui {
 				ShowHide ();
 				ShowHide ();
 			}
 			}
 
 
-		} // WizardStep
+		} // end of WizardStep class
 
 
 		/// <summary>
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 		/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
@@ -306,7 +316,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 		/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 		/// </summary>
 		/// </summary>
-		/// <param name="title">Title for the Wizard.</param>
+		/// <param name="title">Sets the <see cref="Title"/> for the Wizard.</param>
 		/// <remarks>
 		/// <remarks>
 		/// The Wizard will be vertically and horizontally centered in the container.
 		/// 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.
 		/// After initialization use <c>X</c>, <c>Y</c>, <c>Width</c>, and <c>Height</c> change size and position.
@@ -339,6 +349,12 @@ namespace Terminal.Gui {
 
 
 			Loaded += Wizard_Loaded;
 			Loaded += Wizard_Loaded;
 			Closing += Wizard_Closing;
 			Closing += Wizard_Closing;
+
+			if (Modal) {
+				ClearKeybinding (Command.QuitToplevel);
+				AddKeyBinding (Key.Esc, Command.QuitToplevel);
+			}
+
 		}
 		}
 
 
 		private void Wizard_Loaded ()
 		private void Wizard_Loaded ()
@@ -382,16 +398,19 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		///<inheritdoc/>
 		public override bool ProcessKey (KeyEvent kb)
 		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);
 			return base.ProcessKey (kb);
 		}
 		}
 
 
-
 		/// <summary>
 		/// <summary>
 		/// Causes the wizad to move to the next enabled step (or last step if <see cref="CurrentStep"/> is not set). 
 		/// 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.
 		/// If there is no previous step, does nothing.
@@ -548,6 +567,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// <summary>
 		/// The title of the Wizard, shown at the top of the Wizard with " - currentStep.Title" appended.
 		/// The title of the Wizard, shown at the top of the Wizard with " - currentStep.Title" appended.
 		/// </summary>
 		/// </summary>
+		/// <remarks>The Title is only displayed when the <see cref="Wizard"/> is used as a modal pop-up.</remarks>
 		public new ustring Title {
 		public new ustring Title {
 			get {
 			get {
 				// The base (Dialog) Title holds the full title ("Wizard Title - Step Title")
 				// The base (Dialog) Title holds the full title ("Wizard Title - Step Title")
@@ -585,9 +605,9 @@ namespace Terminal.Gui {
 		public event Action<WizardButtonEventArgs> MovingBack;
 		public event Action<WizardButtonEventArgs> MovingBack;
 
 
 		/// <summary>
 		/// <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).
 		/// (otherwise the <see cref="Finished"/> event is raised).
 		/// </summary>
 		/// </summary>
 		public event Action<WizardButtonEventArgs> MovingNext;
 		public event Action<WizardButtonEventArgs> MovingNext;
@@ -600,9 +620,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public event Action<WizardButtonEventArgs> Finished;
 		public event Action<WizardButtonEventArgs> Finished;
 
 
-
 		/// <summary>
 		/// <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>
 		/// </summary>
 		public event Action<WizardButtonEventArgs> Cancelled;
 		public event Action<WizardButtonEventArgs> Cancelled;
 
 
@@ -762,7 +783,15 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
-		/// <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 {
 		public new bool Modal {
 			get => base.Modal;
 			get => base.Modal;
 			set {
 			set {

+ 10 - 2
UICatalog/Scenarios/WizardAsView.cs

@@ -7,7 +7,7 @@ using Terminal.Gui;
 
 
 namespace UICatalog.Scenarios {
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "WizardAsView", Description: "Shows using the Wizard class in an non-modal way")]
 	[ScenarioMetadata (Name: "WizardAsView", Description: "Shows using the Wizard class in an non-modal way")]
-	[ScenarioCategory ("Dialogs")]
+	[ScenarioCategory ("Wizards")]
 	public class WizardAsView : Scenario {
 	public class WizardAsView : Scenario {
 
 
 		public override void Init (Toplevel top, ColorScheme colorScheme)
 		public override void Init (Toplevel top, ColorScheme colorScheme)
@@ -51,6 +51,14 @@ namespace UICatalog.Scenarios {
 				Application.RequestStop ();
 				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
 			// Add 1st step
 			var firstStep = new Wizard.WizardStep ("End User License Agreement");
 			var firstStep = new Wizard.WizardStep ("End User License Agreement");
 			wizard.AddStep (firstStep);
 			wizard.AddStep (firstStep);
@@ -83,7 +91,7 @@ namespace UICatalog.Scenarios {
 			// Add last step
 			// Add last step
 			var lastStep = new Wizard.WizardStep ("The last step");
 			var lastStep = new Wizard.WizardStep ("The last step");
 			wizard.AddStep (lastStep);
 			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
 			// 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.
 			// Current Step. But when running non-modal it must be done manually.

+ 2 - 2
UICatalog/Scenarios/Wizards.cs

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