Просмотр исходного кода

Fixes #1849. Wizard as non-popup is broken (#1853)

* trying to make it work

* Fixes #1849. Wizard as non-modal doesn't work

* Fixes #1855. Window and Frame content view without the margin frame.

* Fixing layout of non-modal

* WizardSTep is now a FrameView

* Now use Modal = false to set visual style automatically

* Removed Controls as an explicit construct. Now just Add to WizardStep

Co-authored-by: BDisp <[email protected]>
Tig Kindel 3 лет назад
Родитель
Сommit
492966048c

+ 6 - 2
Terminal.Gui/Core/Application.cs

@@ -1099,8 +1099,12 @@ namespace Terminal.Gui {
 		{
 			if (_initialized && Driver != null) {
 				var top = new T ();
-				if (top.GetType ().BaseType != typeof (Toplevel)) {
-					throw new ArgumentException (top.GetType ().BaseType.Name);
+				var type = top.GetType ().BaseType;
+				while (type != typeof (Toplevel) && type != typeof (object)) {
+					type = type.BaseType;
+				}
+				if (type != typeof (Toplevel)) {
+					throw new ArgumentException ($"{top.GetType ().Name} must be derived from TopLevel");
 				}
 				Run (top, errorHandler);
 			} else {

+ 1 - 1
Terminal.Gui/Core/Toplevel.cs

@@ -164,7 +164,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Called from <see cref="Application.Begin(Toplevel)"/> before the <see cref="Toplevel"/> is redraws for the first time.
 		/// </summary>
-		internal virtual void OnLoaded ()
+		virtual public void OnLoaded ()
 		{
 			Loaded?.Invoke ();
 		}

+ 1 - 1
Terminal.Gui/Windows/Dialog.cs

@@ -182,7 +182,7 @@ namespace Terminal.Gui {
 					} else {
 						if (i == 0) {
 							// first (leftmost) button - always hard flush left
-							var left = Bounds.Width - 2;
+							var left = Bounds.Width - ((Border.DrawMarginFrame ? 2 : 0) + Border.BorderThickness.Left + Border.BorderThickness.Right);
 							button.X = Pos.AnchorEnd (left);
 						} else {
 							shiftLeft += button.Frame.Width + (spacing);

+ 163 - 96
Terminal.Gui/Windows/Wizard.cs

@@ -11,14 +11,18 @@ namespace Terminal.Gui {
 	/// 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`.
 	/// </remarks>
 	public class Wizard : Dialog {
 
 		/// <summary>
-		/// One step for the Wizard. The <see cref="WizardStep"/> view hosts two sub-views: 1) add <see cref="View"/>s to <see cref="WizardStep.Controls"/>, 
-		/// 2) use <see cref="WizardStep.HelpText"/> to set the contents of the <see cref="TextView"/> that shows on the
-		/// right side. Use <see cref="WizardStep.showControls"/> and <see cref="WizardStep.showHelp"/> to 
-		/// control wether the control or help pane are shown. 
+		/// One step for the 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. 
 		/// </summary>
 		/// <remarks>
 		/// If <see cref="Button"/>s are added, do not set <see cref="Button.IsDefault"/> to true as this will conflict
@@ -29,11 +33,11 @@ namespace Terminal.Gui {
 		/// To enable or disable a step from being shown to the user, set <see cref="View.Enabled"/>.
 		/// 
 		/// </remarks>
-		public class WizardStep : View {
+		public class WizardStep : FrameView {
 			/// <summary>
 			/// The title of the <see cref="WizardStep"/>.
 			/// </summary>
-			public ustring Title {
+			public new ustring Title {
 				get => title;
 				set {
 					if (!OnTitleChanging (title, value)) {
@@ -114,23 +118,21 @@ namespace Terminal.Gui {
 
 			// 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.
-			private View controlPane = new FrameView ();
+			private View contentView = new View ();
 
 			/// <summary>
-			/// THe pane that holds the controls for the <see cref="WizardStep"/>. Use <see cref="WizardStep.Controls"/> `Add(View`) to add 
-			/// controls. Note that the Controls view is sized to take 70% of the Wizard's width and the <see cref="WizardStep.HelpText"/> 
-			/// takes the other 30%. This can be adjusted by setting `Width` from `Dim.Percent(70)` to 
-			/// another value. If <see cref="WizardStep.ShowHelp"/> is set to `false` the control pane will fill the entire 
-			/// Wizard.
-			/// </summary>
-			public View Controls { get => controlPane; }
-
-			/// <summary>
-			/// Sets or gets help text for the <see cref="WizardStep"/>.If <see cref="WizardStep.ShowHelp"/> is set to 
-			/// `false` the control pane will fill the entire wizard.
+			/// 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 => helpTextView.Text = value; }
+			public ustring HelpText { 
+				get => helpTextView.Text; 
+				set { 
+					helpTextView.Text = value;
+					ShowHide ();
+					SetNeedsDisplay ();
+				}
+			}
 			private TextView helpTextView = new TextView ();
 
 			/// <summary>
@@ -158,23 +160,14 @@ namespace Terminal.Gui {
 			public WizardStep (ustring title)
 			{
 				this.Title = title; // this.Title holds just the "Wizard Title"; base.Title holds "Wizard Title - Step Title"
-				this.ColorScheme = Colors.Dialog;
-
-				Y = 0;
-				Height = Dim.Fill (1); // for button frame
-				Width = Dim.Fill ();
+				this.Border.BorderStyle = BorderStyle.Rounded;
 
-				Controls.ColorScheme = Colors.Dialog;
-				Controls.Border.BorderStyle = BorderStyle.None;
-				Controls.Border.Padding = new Thickness (0);
-				Controls.Border.BorderThickness = new Thickness (0);
-				this.Add (Controls);
+				base.Add (contentView);
 
-				helpTextView.ColorScheme = Colors.Menu;
-				helpTextView.Y = 0;
+				helpTextView.ColorScheme = Colors.TopLevel;
 				helpTextView.ReadOnly = true;
 				helpTextView.WordWrap = true;
-				this.Add (helpTextView);
+				base.Add (helpTextView);
 
 				ShowHide ();
 
@@ -222,70 +215,79 @@ namespace Terminal.Gui {
 					scrollBar.LayoutSubviews ();
 					scrollBar.Refresh ();
 				};
-				this.Add (scrollBar);
-			}
-
-			//public override void OnEnabledChanged()
-			//{
-			//	if (Enabled) { }
-			//	base.OnEnabledChanged ();
-			//}
-
-
-			/// <summary>
-			/// If true (the default) the help will be visible. If false, the help will not be shown and the control pane will
-			/// fill the wizard step.
-			/// </summary>
-			public bool ShowHelp {
-				get => showHelp;
-				set {
-					showHelp = value;
-					ShowHide ();
-				}
-			}
-			private bool showHelp = true;
-
-			/// <summary>
-			/// If true (the default) the <see cref="Controls"/> View will be visible. If false, the controls will not be shown and the help will
-			/// fill the wizard step.
-			/// </summary>
-			public bool ShowControls {
-				get => showControls;
-				set {
-					showControls = value;
-					ShowHide ();
-				}
+				base.Add (scrollBar);
 			}
-			private bool showControls = true;
 
 			/// <summary>
-			/// Does the work to show and hide the controls, help, and buttons as appropriate
+			/// Does the work to show and hide the contentView and helpView as appropriate
 			/// </summary>
-			private void ShowHide ()
+			internal void ShowHide ()
 			{
-				Controls.Height = Dim.Fill (1);
-				helpTextView.Height = Dim.Fill (1);
+				contentView.Height = Dim.Fill ();
+				helpTextView.Height = Dim.Fill ();
 				helpTextView.Width = Dim.Fill ();
 
-				if (showControls) {
-					if (showHelp) {
-						Controls.Width = Dim.Percent (70);
-						helpTextView.X = Pos.Right (Controls);
+				if (contentView.InternalSubviews?.Count > 0) {
+					if (helpTextView.Text.Length > 0) {
+						contentView.Width = Dim.Percent (70);
+						helpTextView.X = Pos.Right (contentView);
 						helpTextView.Width = Dim.Fill ();
 
 					} else {
-						Controls.Width = Dim.Percent (100);
+						contentView.Width = Dim.Percent (100);
 					}
 				} else {
-					if (showHelp) {
+					if (helpTextView.Text.Length > 0) {
 						helpTextView.X = 0;
 					} else {
 						// Error - no pane shown
 					}
 
 				}
-				Controls.Visible = showControls;
-				helpTextView.Visible = showHelp;
+				contentView.Visible = contentView.InternalSubviews?.Count > 0;
+				helpTextView.Visible = helpTextView.Text.Length > 0;
+			}
+
+			/// <summary>
+			/// Add the specified <see cref="View"/> to the <see cref="WizardStep"/>. 
+			/// </summary>
+			/// <param name="view"><see cref="View"/> to add to this container</param>
+			public override void Add (View view)
+			{
+				contentView.Add (view);
+				if (view.CanFocus)
+					CanFocus = true;
+				ShowHide ();
+			}
+
+			/// <summary>
+			///   Removes a <see cref="View"/> from <see cref="WizardStep"/>.
+			/// </summary>
+			/// <remarks>
+			/// </remarks>
+			public override void Remove (View view)
+			{
+				if (view == null)
+					return;
+
+				SetNeedsDisplay ();
+				var touched = view.Frame;
+				contentView.Remove (view);
+
+				if (contentView.InternalSubviews.Count < 1)
+					this.CanFocus = false;
+				ShowHide ();
+			}
+
+			/// <summary>
+			///   Removes all <see cref="View"/>s from the <see cref="WizardStep"/>.
+			/// </summary>
+			/// <remarks>
+			/// </remarks>
+			public override void RemoveAll ()
+			{
+				contentView.RemoveAll ();
+				ShowHide ();
 			}
 
 		} // WizardStep
@@ -316,12 +318,13 @@ namespace Terminal.Gui {
 			// the left and right edge
 			ButtonAlignment = ButtonAlignments.Justify;
 			this.Border.BorderStyle = BorderStyle.Double;
+			this.Border.Padding = new Thickness (0);
 
-			// Add a horiz separator
-			var separator = new LineView (Graphs.Orientation.Horizontal) {
-				Y = Pos.AnchorEnd (2)
-			};
-			Add (separator);
+			//// Add a horiz separator
+			//var separator = new LineView (Graphs.Orientation.Horizontal) {
+			//	Y = Pos.AnchorEnd (2)
+			//};
+			//Add (separator);
 
 			// BUGBUG: Space is to work around https://github.com/migueldeicaza/gui.cs/issues/1812
 			backBtn = new Button (Strings.wzBack) { AutoSize = true };
@@ -338,6 +341,11 @@ namespace Terminal.Gui {
 			Closing += Wizard_Closing;
 		}
 
+		private void Wizard_Loaded ()
+		{
+			CurrentStep = GetFirstStep (); // gets the first step if CurrentStep == null
+		}
+
 		private bool finishedPressed = false;
 
 		private void Wizard_Closing (ToplevelClosingEventArgs obj)
@@ -348,22 +356,19 @@ namespace Terminal.Gui {
 			}
 		}
 
-		private void Wizard_Loaded ()
-		{
-			foreach (var step in steps) {
-				step.Y = 0;
-			}
-			CurrentStep = GetNextStep (); // gets the first step if CurrentStep == null
-		}
-
 		private void NextfinishBtn_Clicked ()
 		{
-			if (CurrentStep == GetLastStep()) {
+			if (CurrentStep == GetLastStep ()) {
 				var args = new WizardButtonEventArgs ();
 				Finished?.Invoke (args);
 				if (!args.Cancel) {
 					finishedPressed = true;
-					Application.RequestStop (this);
+					if (IsCurrentTop) {
+						Application.RequestStop (this);
+					} else {
+						// Wizard was created as a non-modal (just added to another View). 
+						// Do nothing
+					}
 				}
 			} else {
 				var args = new WizardButtonEventArgs ();
@@ -374,6 +379,19 @@ namespace Terminal.Gui {
 			}
 		}
 
+		///<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;
+			}
+			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.
@@ -396,7 +414,7 @@ namespace Terminal.Gui {
 		{
 			LinkedListNode<WizardStep> step = null;
 			if (CurrentStep == null) {
-				// Get last step, assume it is next
+				// Get first step, assume it is next
 				step = steps.First;
 			} else {
 				// Get the step after current
@@ -518,10 +536,12 @@ namespace Terminal.Gui {
 		/// <remarks>The "Next..." button of the last step added will read "Finish" (unless changed from default).</remarks>
 		public void AddStep (WizardStep newStep)
 		{
-			steps.AddLast (newStep);
-			this.Add (newStep);
+			SizeStep (newStep);
+
 			newStep.EnabledChanged += UpdateButtonsAndTitle;
 			newStep.TitleChanged += (args) => UpdateButtonsAndTitle ();
+			steps.AddLast (newStep);
+			this.Add (newStep);
 			UpdateButtonsAndTitle ();
 		}
 
@@ -679,6 +699,7 @@ namespace Terminal.Gui {
 			// Hide all but the new step
 			foreach (WizardStep step in steps) {
 				step.Visible = (step == newStep);
+				step.ShowHide ();
 			}
 
 			var oldStep = currentStep;
@@ -717,9 +738,55 @@ namespace Terminal.Gui {
 			} else {
 				nextfinishBtn.Text = CurrentStep.NextButtonText != ustring.Empty ? CurrentStep.NextButtonText : Strings.wzNext; // "_Next...";
 			}
+
+			SizeStep (CurrentStep);
+
 			SetNeedsLayout ();
 			LayoutSubviews ();
 			Redraw (Bounds);
 		}
+
+		private void SizeStep (WizardStep step)
+		{
+			if (Modal) {
+				// If we're modal, then we expand the WizardStep so that the top and side 
+				// borders and not visible. The bottom border is the separator above the buttons.
+				step.X = step.Y = -1;
+				step.Height = Dim.Fill (1); // for button frame
+				step.Width = Dim.Fill (-1);
+			} else {
+				// If we're not a modal, then we show the border around the WizardStep
+				step.X = step.Y = 0;
+				step.Height = Dim.Fill (1); // for button frame
+				step.Width = Dim.Fill (0);
+			}
+		}
+
+		/// <inheritdoc/>
+		public new bool Modal {
+			get => base.Modal;
+			set {
+				base.Modal = value;
+				foreach (var step in steps) {
+					SizeStep (step);
+				}
+				if (base.Modal) {
+					ColorScheme = Colors.Dialog;
+					Border.BorderStyle = BorderStyle.Rounded;
+					Border.Effect3D = true;
+					Border.DrawMarginFrame = true;
+				} else {
+					if (SuperView != null) {
+						ColorScheme = SuperView.ColorScheme;
+					} else {
+						ColorScheme = Colors.Base;
+					}
+					CanFocus = true;
+					Border.Effect3D = false;
+					Border.BorderStyle = BorderStyle.None;
+					Border.DrawMarginFrame = false;
+				}
+			}
+		}
 	}
 }

+ 4 - 0
UICatalog/Properties/launchSettings.json

@@ -18,6 +18,10 @@
     "Buttons": {
       "commandName": "Project",
       "commandLineArgs": "Buttons"
+    },
+    "WizardAsView": {
+      "commandName": "Project",
+      "commandLineArgs": "WizardAsView"
     }
   }
 }

+ 7 - 2
UICatalog/Scenarios/Dialogs.cs

@@ -146,6 +146,8 @@ namespace UICatalog.Scenarios {
 			};
 			showDialogButton.Clicked += () => {
 				try {
+					Dialog dialog = null;
+
 					int width = 0;
 					int.TryParse (widthEdit.Text.ToString (), out width);
 					int height = 0;
@@ -182,7 +184,7 @@ namespace UICatalog.Scenarios {
 
 					// This tests dynamically adding buttons; ensuring the dialog resizes if needed and 
 					// the buttons are laid out correctly
-					var dialog = new Dialog (titleEdit.Text, width, height,
+					dialog = new Dialog (titleEdit.Text, width, height,
 						buttons.ToArray ()) {
 						ButtonAlignment = (Dialog.ButtonAlignments)styleRadioGroup.SelectedItem
 					};
@@ -204,6 +206,7 @@ namespace UICatalog.Scenarios {
 						button.Clicked += () => {
 							clicked = buttonId;
 							Application.RequestStop ();
+
 						};
 						buttons.Add (button);
 						dialog.AddButton (button);
@@ -223,10 +226,12 @@ namespace UICatalog.Scenarios {
 						}
 						dialog.LayoutSubviews ();
 					};
+					dialog.Closed += (args) => {
+						buttonPressedLabel.Text = $"{clicked}";
+					};
 					dialog.Add (addChar);
 
 					Application.Run (dialog);
-					buttonPressedLabel.Text = $"{clicked}";
 
 				} catch (FormatException) {
 					buttonPressedLabel.Text = "Invalid Options";

+ 102 - 0
UICatalog/Scenarios/WizardAsView.cs

@@ -0,0 +1,102 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios {
+	[ScenarioMetadata (Name: "WizardAsView", Description: "Shows using the Wizard class in an non-modal way")]
+	[ScenarioCategory ("Dialogs")]
+	public class WizardAsView : Scenario {
+
+		public override void Init (Toplevel top, ColorScheme colorScheme)
+		{
+			Top = Application.Top;
+
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_Restart Configuration...", "", () => MessageBox.Query ("Wizaard", "Are you sure you want to reset the Wizard and start over?", "Ok", "Cancel")),
+					new MenuItem ("Re_boot Server...", "", () => MessageBox.Query ("Wizaard", "Are you sure you want to reboot the server start over?", "Ok", "Cancel")),
+					new MenuItem ("_Shutdown Server...", "", () => MessageBox.Query ("Wizaard", "Are you sure you want to cancel setup and shutdown?", "Ok", "Cancel")),
+				})
+			});
+			Top.Add (menu);
+
+			// No need for a Title because the border is disabled
+			var wizard = new Wizard () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+			};
+
+			// Set Mdoal to false to cause the Wizard class to render without a frame and
+			// behave like an non-modal View (vs. a modal/pop-up Window).
+			wizard.Modal = false;
+
+			wizard.MovingBack += (args) => {
+				//args.Cancel = true;
+				//actionLabel.Text = "Moving Back";
+			};
+
+			wizard.MovingNext += (args) => {
+				//args.Cancel = true;
+				//actionLabel.Text = "Moving Next";
+			};
+
+			wizard.Finished += (args) => {
+				//args.Cancel = true;
+				MessageBox.Query ("Setup Wizard", "Finished", "Ok");
+				Application.RequestStop ();
+			};
+
+			// Add 1st step
+			var firstStep = new Wizard.WizardStep ("End User License Agreement");
+			wizard.AddStep (firstStep);
+			firstStep.NextButtonText = "Accept!";
+			firstStep.HelpText = "This is the End User License Agreement.\n\n\n\n\n\nThis is a test of the emergency broadcast system. This is a test of the emergency broadcast system.\nThis is a test of the emergency broadcast system.\n\n\nThis is a test of the emergency broadcast system.\n\nThis is a test of the emergency broadcast system.\n\n\n\nThe end of the EULA.";
+
+			// Add 2nd step
+			var secondStep = new Wizard.WizardStep ("Second Step");
+			wizard.AddStep (secondStep);
+			secondStep.HelpText = "This is the help text for the Second Step.\n\nPress the button to change the Title.\n\nIf First Name is empty the step will prevent moving to the next step.";
+
+			var buttonLbl = new Label () { Text = "Second Step Button: ", X = 0, Y = 0 };
+			var button = new Button () {
+				Text = "Press Me to Rename Step",
+				X = Pos.Right (buttonLbl),
+				Y = Pos.Top (buttonLbl)
+			};
+			button.Clicked += () => {
+				secondStep.Title = "2nd Step";
+				MessageBox.Query ("Wizard Scenario", "This Wizard Step's title was changed to '2nd Step'", "Ok");
+			};
+			secondStep.Add (buttonLbl, button);
+			var lbl = new Label () { Text = "First Name: ", X = Pos.Left (buttonLbl), Y = Pos.Bottom (buttonLbl) };
+			var firstNameField = new TextField () { Text = "Number", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
+			secondStep.Add (lbl, firstNameField);
+			lbl = new Label () { Text = "Last Name:  ", X = Pos.Left (buttonLbl), Y = Pos.Bottom (lbl) };
+			var lastNameField = new TextField () { Text = "Six", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
+			secondStep.Add (lbl, lastNameField);
+
+			// 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.";
+
+			// 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.
+			wizard.CurrentStep = wizard.GetNextStep ();
+
+			Top.Add (wizard);
+			Application.Run (Top);
+		}
+
+		public override void Run ()
+		{
+			// Do nothing in the override because we call Application.Run above
+			// (just to make it clear how the Top is being run and not the Wizard).
+		}
+	}
+}

+ 68 - 45
UICatalog/Scenarios/Wizards.cs

@@ -6,12 +6,14 @@ using System.Text;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
-	[ScenarioMetadata (Name: "Wizards", Description: "Demonstrates how to the Wizard class")]
+	[ScenarioMetadata (Name: "Wizards", Description: "Demonstrates the Wizard class")]
 	[ScenarioCategory ("Dialogs")]
 	public class Wizards : Scenario {
 		public override void Setup ()
 		{
+			// Set the colorschem to Base so the non-modal part of the demo looks right
 			Win.ColorScheme = Colors.Base;
+
 			var frame = new FrameView ("Wizard Options") {
 				X = Pos.Center (),
 				Y = 0,
@@ -71,18 +73,9 @@ namespace UICatalog.Scenarios {
 			};
 			frame.Add (titleEdit);
 
-			var useStepView = new CheckBox () {
-				Text = "Add 3rd step controls to WizardStep instead of WizardStep.Controls",
-				Checked = false,
-				X = Pos.Left (titleEdit),
-				Y = Pos.Bottom (titleEdit)
-			};
-			frame.Add (useStepView);
-
-
 			void Top_Loaded ()
 			{
-				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) + Dim.Height (useStepView) + 2;
+				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) + 2;
 				Top.Loaded -= Top_Loaded;
 			}
 			Top.Loaded += Top_Loaded;
@@ -93,17 +86,28 @@ namespace UICatalog.Scenarios {
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
 			};
 			Win.Add (label);
-			var actionLabel = new Label (" ") {
+
+			var actionLabel = new Label ("") {
 				X = Pos.Right (label),
 				Y = Pos.AnchorEnd (1),
 				ColorScheme = Colors.Error,
 			};
+			Win.Add (actionLabel);
 
-			var showWizardButton = new Button ("Show Wizard") {
+			var modalCheckBox = new CheckBox ("Modal (pop-up)") {
 				X = Pos.Center (),
 				Y = Pos.Bottom (frame) + 2,
+				AutoSize = true,
+				Checked = true
+			};
+			Win.Add (modalCheckBox);
+
+			var showWizardButton = new Button ("Show Wizard") {
+				X = Pos.Center (),
+				Y = Pos.Bottom (modalCheckBox) + 1,
 				IsDefault = true,
 			};
+
 			showWizardButton.Clicked += () => {
 				try {
 					int width = 0;
@@ -116,6 +120,8 @@ namespace UICatalog.Scenarios {
 						return;
 					}
 
+					actionLabel.Text = ustring.Empty;
+
 					var wizard = new Wizard (titleEdit.Text) {
 						Width = width,
 						Height = height
@@ -143,22 +149,14 @@ namespace UICatalog.Scenarios {
 
 					// Add 1st step
 					var firstStep = new Wizard.WizardStep ("End User License Agreement");
-					wizard.AddStep (firstStep);
-					firstStep.ShowControls = false;
 					firstStep.NextButtonText = "Accept!";
 					firstStep.HelpText = "This is the End User License Agreement.\n\n\n\n\n\nThis is a test of the emergency broadcast system. This is a test of the emergency broadcast system.\nThis is a test of the emergency broadcast system.\n\n\nThis is a test of the emergency broadcast system.\n\nThis is a test of the emergency broadcast system.\n\n\n\nThe end of the EULA.";
+					wizard.AddStep (firstStep);
 
 					// Add 2nd step
 					var secondStep = new Wizard.WizardStep ("Second Step");
 					wizard.AddStep (secondStep);
-					secondStep.HelpText = "This is the help text for the Second Step.\n\nPress the button demo changing the Title.\n\nIf First Name is empty the step will prevent moving to the next step.";
-
-					View viewForControls = secondStep.Controls;
-					ustring frameMsg = "Added to WizardStep.Controls";
-					if (useStepView.Checked) {
-						viewForControls = secondStep;
-						frameMsg = "Added to WizardStep directly";
-					}
+					secondStep.HelpText = "This is the help text for the Second Step.\n\nPress the button to change the Title.\n\nIf First Name is empty the step will prevent moving to the next step.";
 
 					var buttonLbl = new Label () { Text = "Second Step Button: ", X = 1, Y = 1 };
 					var button = new Button () {
@@ -170,28 +168,25 @@ namespace UICatalog.Scenarios {
 						secondStep.Title = "2nd Step";
 						MessageBox.Query ("Wizard Scenario", "This Wizard Step's title was changed to '2nd Step'");
 					};
-					viewForControls.Add (buttonLbl, button);
+					secondStep.Add (buttonLbl, button);
 					var lbl = new Label () { Text = "First Name: ", X = 1, Y = Pos.Bottom (buttonLbl) };
 					var firstNameField = new TextField () { Text = "Number", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
-					viewForControls.Add (lbl, firstNameField);
+					secondStep.Add (lbl, firstNameField);
 					lbl = new Label () { Text = "Last Name:  ", X = 1, Y = Pos.Bottom (lbl) };
 					var lastNameField = new TextField () { Text = "Six", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) };
-					viewForControls.Add (lbl, lastNameField);
+					secondStep.Add (lbl, lastNameField);
 					var thirdStepEnabledCeckBox = new CheckBox () { Text = "Enable Step _3", Checked = false, X = Pos.Left (lastNameField), Y = Pos.Bottom (lastNameField) };
-					viewForControls.Add (thirdStepEnabledCeckBox);
+					secondStep.Add (thirdStepEnabledCeckBox);
 
-					// Add a frame to demonstrate difference between adding controls to
-					// WizardStep.Controls vs. WizardStep directly. This is here to demonstrate why 
-					// adding to .Controls is preferred.
-					var frame = new FrameView ($"A Broken Frame - {frameMsg}") {
+					// Add a frame 
+					var frame = new FrameView ($"A Broken Frame (by Depeche Mode)") {
 						X = 0,
 						Y = Pos.Bottom (thirdStepEnabledCeckBox) + 2,
 						Width = Dim.Fill (),
-						Height = 4,
-						//ColorScheme = Colors.Error,
+						Height = 4
 					};
 					frame.Add (new TextField ("This is a TextField inside of the frame."));
-					viewForControls.Add (frame);
+					secondStep.Add (frame);
 					wizard.StepChanging += (args) => {
 						if (args.OldStep == secondStep && firstNameField.Text.IsEmpty) {
 							args.Cancel = true;
@@ -208,7 +203,7 @@ namespace UICatalog.Scenarios {
 						X = 0,
 						Y = 0
 					};
-					thirdStep.Controls.Add (step3Label);
+					thirdStep.Add (step3Label);
 					var progLbl = new Label () { Text = "Third Step ProgressBar: ", X = 1, Y = 10 };
 					var progressBar = new ProgressBar () {
 						X = Pos.Right (progLbl),
@@ -216,7 +211,7 @@ namespace UICatalog.Scenarios {
 						Width = 40,
 						Fraction = 0.42F
 					};
-					thirdStep.Controls.Add (progLbl, progressBar);
+					thirdStep.Add (progLbl, progressBar);
 					thirdStep.Enabled = thirdStepEnabledCeckBox.Checked;
 					thirdStepEnabledCeckBox.Toggled += (args) => {
 						thirdStep.Enabled = thirdStepEnabledCeckBox.Checked;
@@ -225,17 +220,30 @@ namespace UICatalog.Scenarios {
 					// Add 4th step
 					var fourthStep = new Wizard.WizardStep ("Step Four");
 					wizard.AddStep (fourthStep);
-					fourthStep.ShowHelp = false;
 					var someText = new TextView () {
-						Text = "This step (Step Four) shows how to hide the Help pane. The control pane contains this TextView (but it's hard to tell it's a TextView because of Issue #1800).",
+						Text = "This step (Step Four) shows how to show/hide the Help pane. The step contains this TextView (but it's hard to tell it's a TextView because of Issue #1800).",
 						X = 0,
 						Y = 0,
 						Width = Dim.Fill (),
-						Height = Dim.Fill (),
+						Height = Dim.Fill (1),
 						WordWrap = true,
-						AllowsTab = false
+						AllowsTab = false,
+					};
+					var help = "This is helpful.";
+					fourthStep.Add (someText);
+					var hideHelpBtn = new Button () {
+						Text = "Press me to show/hide help",
+						X = Pos.Center (),
+						Y = Pos.AnchorEnd(1)
+					};
+					hideHelpBtn.Clicked += () => {
+						if (fourthStep.HelpText.Length > 0) {
+							fourthStep.HelpText = ustring.Empty;
+						} else {
+							fourthStep.HelpText = help;
+						}
 					};
-					fourthStep.Controls.Add (someText);
+					fourthStep.Add (hideHelpBtn);
 					fourthStep.NextButtonText = "Go To Last Step";
 					var scrollBar = new ScrollBarView (someText, true);
 
@@ -265,7 +273,7 @@ namespace UICatalog.Scenarios {
 						scrollBar.LayoutSubviews ();
 						scrollBar.Refresh ();
 					};
-					fourthStep.Controls.Add (scrollBar);
+					fourthStep.Add (scrollBar);
 
 					// Add last step
 					var lastStep = new Wizard.WizardStep ("The last step");
@@ -283,15 +291,30 @@ namespace UICatalog.Scenarios {
 						finalFinalStep.Enabled = finalFinalStepEnabledCeckBox.Checked;
 					};
 
-					Application.Run (wizard);
+					if (modalCheckBox.Checked) {
+						Application.Run (wizard);
+					} else {
+						// Disable the Show button so this only happens once
+						showWizardButton.Visible = false;
+						// To use Wizard as a View, you must set Modal = false
+						wizard.Modal = false;
+
+						// 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.
+						wizard.CurrentStep = wizard.GetNextStep ();
+
+						Win.Add (wizard);
+						
+						// Ensure the wizard has focus
+						wizard.SetFocus ();
+						
+					}
 
 				} catch (FormatException) {
 					actionLabel.Text = "Invalid Options";
 				}
 			};
 			Win.Add (showWizardButton);
-
-			Win.Add (actionLabel);
 		}
 	}
 }

+ 1 - 1
UnitTests/WizardTests.cs

@@ -121,7 +121,7 @@ namespace Terminal.Gui.Views {
 			var topRow = $"{d.ULDCorner} {title}{stepTitle} {new String (d.HDLine.ToString () [0], width - title.Length - stepTitle.Length - 4)}{d.URDCorner}";
 			var row2 = $"{d.VDLine}{new String (' ', width - 2)}{d.VDLine}";
 			var row3 = row2;
-			var separatorRow = $"{d.VDLine}{new String (d.HLine.ToString () [0], width - 2)}{d.VDLine}";
+			var separatorRow = $"{d.VDLine}{new String (' ', width - 2)}{d.VDLine}";
 			var buttonRow = $"{d.VDLine}{btnBack}{new String (' ', width - btnBack.Length - btnNext.Length - 2)}{btnNext}{d.VDLine}";
 			var bottomRow = $"{d.LLDCorner}{new String (d.HDLine.ToString () [0], width - 2)}{d.LRDCorner}";