Explorar o código

Fixes #355 stack overflow with Pos based on the size of windows at startup. Added a OnResized action to set the Pos after the terminal are resized. (#367)

BDisp %!s(int64=5) %!d(string=hai) anos
pai
achega
ee7fc3022d
Modificáronse 2 ficheiros con 55 adicións e 45 borrados
  1. 10 6
      Example/demo.cs
  2. 45 39
      Terminal.Gui/Core.cs

+ 10 - 6
Example/demo.cs

@@ -88,7 +88,7 @@ static class Demo {
 			"Text Alignments", 50, 20,
 			new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
 			new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
-		
+
 
 		int i = 0;
 		string txt = "Hello world, how are you doing today";
@@ -418,6 +418,8 @@ static class Demo {
 			CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
 
 		//Application.UseSystemConsole = true;
+		Console.WindowHeight = 35;
+
 		Application.Init ();
 
 		var top = Application.Top;
@@ -521,14 +523,16 @@ static class Demo {
 		});
 
 		win.Add (drag, dragText);
-#if false
+#if true
 		// This currently causes a stack overflow, because it is referencing a window that has not had its size allocated yet
 
-		var bottom = new Label ("This should go on the bottom!") {
-			X = Pos.Left (win),
-			Y = Pos.Bottom (win)
-		};
+		var bottom = new Label ("This should go on the bottom!");
 		win.Add (bottom);
+
+		Application.OnResized = () => {
+			bottom.X = Pos.Left (win);
+			bottom.Y = Pos.Bottom (win);
+		};
 #endif
 
 		top.Add (win);

+ 45 - 39
Terminal.Gui/Core.cs

@@ -68,7 +68,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		///   If the view is focused, gives the view a
-		///   chance to process the keystroke. 
+		///   chance to process the keystroke.
 		/// </summary>
 		/// <remarks>
 		///   <para>
@@ -83,7 +83,7 @@ namespace Terminal.Gui {
 		///   </para>
 		///   <para>
 		///     The View implementation does nothing but return false,
-		///     so it is not necessary to call base.ProcessKey if you 
+		///     so it is not necessary to call base.ProcessKey if you
 		///     derive directly from View, but you should if you derive
 		///     other View subclasses.
 		///   </para>
@@ -161,27 +161,27 @@ namespace Terminal.Gui {
 	/// <para>
 	///    Views can either be created with an absolute position, by calling the constructor that takes a
 	///    Rect parameter to specify the absolute position and size (the Frame of the View) or by setting the
-	///    X, Y, Width and Height properties on the view.    Both approaches use coordinates that are relative 
+	///    X, Y, Width and Height properties on the view.    Both approaches use coordinates that are relative
 	///    to the container they are being added to.
 	/// </para>
 	/// <para>
-	///    When you do not specify a Rect frame you can use the more flexible 
-	///    Dim and Pos objects that can dynamically update the position of a view.   
+	///    When you do not specify a Rect frame you can use the more flexible
+	///    Dim and Pos objects that can dynamically update the position of a view.
 	///    The X and Y properties are of type <see cref="T:Terminal.Gui.Pos"/>
 	///    and you can use either absolute positions, percentages or anchor
-	///    points.   The Width and Height properties are of type 
-	///    <see cref="T:Terminal.Gui.Dim"/> and can use absolute position, 
+	///    points.   The Width and Height properties are of type
+	///    <see cref="T:Terminal.Gui.Dim"/> and can use absolute position,
 	///    percentages and anchors.  These are useful as they will take
 	///    care of repositioning your views if your view's frames are resized
 	///    or if the terminal size changes.
 	/// </para>
 	/// <para>
-	///    When you specify the Rect parameter to a view, you are setting the LayoutStyle to Absolute, and the 
-	///    view will always stay in the position that you placed it.   To change the position change the 
+	///    When you specify the Rect parameter to a view, you are setting the LayoutStyle to Absolute, and the
+	///    view will always stay in the position that you placed it.   To change the position change the
 	///    Frame property to the new position.
 	/// </para>
 	/// <para>
-	///    Subviews can be added to a View by calling the Add method.   The container of a view is the 
+	///    Subviews can be added to a View by calling the Add method.   The container of a view is the
 	///    Superview.
 	/// </para>
 	/// <para>
@@ -192,7 +192,7 @@ namespace Terminal.Gui {
 	///    Views have a ColorScheme property that defines the default colors that subviews
 	///    should use for rendering.   This ensures that the views fit in the context where
 	///    they are being used, and allows for themes to be plugged in.   For example, the
-	///    default colors for windows and toplevels uses a blue background, while it uses 
+	///    default colors for windows and toplevels uses a blue background, while it uses
 	///    a white background for dialog boxes and a red background for errors.
 	/// </para>
 	/// <para>
@@ -208,7 +208,7 @@ namespace Terminal.Gui {
 	/// <para>
 	///    Views that are focusable should implement the PositionCursor to make sure that
 	///    the cursor is placed in a location that makes sense.   Unix terminals do not have
-	///    a way of hiding the cursor, so it can be distracting to have the cursor left at 
+	///    a way of hiding the cursor, so it can be distracting to have the cursor left at
 	///    the last focused view.   So views should make sure that they place the cursor
 	///    in a visually sensible place.
 	/// </para>
@@ -257,7 +257,7 @@ namespace Terminal.Gui {
 
 		static IList<View> empty = new List<View> (0).AsReadOnly ();
 
-		// This is null, and allocated on demand.  
+		// This is null, and allocated on demand.
 		List<View> subviews;
 
 		/// <summary>
@@ -292,7 +292,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <value>The frame.</value>
 		/// <remarks>
-		///    Altering the Frame of a view will trigger the redrawing of the 
+		///    Altering the Frame of a view will trigger the redrawing of the
 		///    view as well as the redrawing of the affected regions in the superview.
 		/// </remarks>
 		public virtual Rect Frame {
@@ -422,7 +422,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.View"/> class and sets the
-		/// view up for Computed layout, which will use the values in X, Y, Width and Height to 
+		/// view up for Computed layout, which will use the values in X, Y, Width and Height to
 		/// compute the View's Frame.
 		/// </summary>
 		public View ()
@@ -1233,8 +1233,8 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// This virtual method is invoked when a view starts executing or 
-		/// when the dimensions of the view have changed, for example in 
+		/// This virtual method is invoked when a view starts executing or
+		/// when the dimensions of the view have changed, for example in
 		/// response to the container view or terminal resizing.
 		/// </summary>
 		public virtual void LayoutSubviews ()
@@ -1269,8 +1269,8 @@ namespace Terminal.Gui {
 				if (v.LayoutStyle == LayoutStyle.Computed)
 					v.RelativeLayout (Frame);
 
-				
-				v.LayoutSubviews ();
+				if (this?.SuperView != v)
+					v.LayoutSubviews ();
 				v.layoutNeeded = false;
 
 			}
@@ -1304,23 +1304,23 @@ namespace Terminal.Gui {
 	///     new toplevel.
 	///   </para>
 	///   <para>
-	///     TopLevels can also opt-in to more sophisticated initialization 
-	///     by implementing <see cref="ISupportInitialize"/>. When they do 
-	///     so, the <see cref="ISupportInitialize.BeginInit"/> and 
-	///     <see cref="ISupportInitialize.EndInit"/> methods will be called 
+	///     TopLevels can also opt-in to more sophisticated initialization
+	///     by implementing <see cref="ISupportInitialize"/>. When they do
+	///     so, the <see cref="ISupportInitialize.BeginInit"/> and
+	///     <see cref="ISupportInitialize.EndInit"/> methods will be called
 	///     before running the view.
-	///     If first-run-only initialization is preferred, the <see cref="ISupportInitializeNotification"/> 
-	///     can be implemented too, in which case the <see cref="ISupportInitialize"/> 
-	///     methods will only be called if <see cref="ISupportInitializeNotification.IsInitialized"/> 
-	///     is <see langword="false"/>. This allows proper View inheritance hierarchies 
-	///     to override base class layout code optimally by doing so only on first run, 
+	///     If first-run-only initialization is preferred, the <see cref="ISupportInitializeNotification"/>
+	///     can be implemented too, in which case the <see cref="ISupportInitialize"/>
+	///     methods will only be called if <see cref="ISupportInitializeNotification.IsInitialized"/>
+	///     is <see langword="false"/>. This allows proper View inheritance hierarchies
+	///     to override base class layout code optimally by doing so only on first run,
 	///     instead of on every run.
 	///   </para>
 	/// </remarks>
 	public class Toplevel : View {
 		/// <summary>
 		/// This flag is checked on each iteration of the mainloop and it continues
-		/// running until this flag is set to false.   
+		/// running until this flag is set to false.
 		/// </summary>
 		public bool Running;
 
@@ -1362,8 +1362,8 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Determines whether the <see cref="Toplevel"/> is modal or not. 
-		/// Causes <see cref="ProcessKey(KeyEvent)"/> to propagate keys upwards 
+		/// Determines whether the <see cref="Toplevel"/> is modal or not.
+		/// Causes <see cref="ProcessKey(KeyEvent)"/> to propagate keys upwards
 		/// by default unless set to <see langword="true"/>.
 		/// </summary>
 		public bool Modal { get; set; }
@@ -1592,7 +1592,7 @@ namespace Terminal.Gui {
 		int padding;
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Window"/> with
-		/// the specified frame for its location, with the specified border 
+		/// the specified frame for its location, with the specified border
 		/// an optional title.
 		/// </summary>
 		/// <param name="frame">Frame.</param>
@@ -1610,7 +1610,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Window"/> with
-		/// the specified frame for its location, with the specified border 
+		/// the specified frame for its location, with the specified border
 		/// an optional title.
 		/// </summary>
 		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
@@ -1771,7 +1771,7 @@ namespace Terminal.Gui {
 	/// </summary>
 	/// <remarks>
 	///   <para>
-	///     You can hook up to the Iteration event to have your method 
+	///     You can hook up to the Iteration event to have your method
 	///     invoked on each iteration of the mainloop.
 	///   </para>
 	///   <para>
@@ -1811,7 +1811,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		///   This event is raised on each iteration of the
-		///   main loop. 
+		///   main loop.
 		/// </summary>
 		/// <remarks>
 		///   See also <see cref="Timeout"/>
@@ -2069,7 +2069,7 @@ namespace Terminal.Gui {
 		/// <param name="toplevel">Toplevel to prepare execution for.</param>
 		/// <remarks>
 		///  This method prepares the provided toplevel for running with the focus,
-		///  it adds this to the list of toplevels, sets up the mainloop to process the 
+		///  it adds this to the list of toplevels, sets up the mainloop to process the
 		///  event, lays out the subviews, focuses the first element, and draws the
 		///  toplevel in the screen.   This is usually followed by executing
 		///  the <see cref="RunLoop"/> method, and then the <see cref="End(RunState)"/> method upon termination which will
@@ -2082,7 +2082,7 @@ namespace Terminal.Gui {
 			var rs = new RunState (toplevel);
 
 			Init ();
-			if (toplevel is ISupportInitializeNotification initializableNotification && 
+			if (toplevel is ISupportInitializeNotification initializableNotification &&
 			    !initializableNotification.IsInitialized) {
 				initializableNotification.BeginInit();
 				initializableNotification.EndInit();
@@ -2172,7 +2172,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <remarks>
 		///   Use the wait parameter to control whether this is a
-		///   blocking or non-blocking call.   
+		///   blocking or non-blocking call.
 		/// </remarks>
 		/// <param name="state">The state returned by the Begin method.</param>
 		/// <param name="wait">By default this is true which will execute the runloop waiting for events, if you pass false, you can use this method to run a single iteration of the events.</param>
@@ -2246,7 +2246,7 @@ namespace Terminal.Gui {
 		///     returned value, and then calling end on the return value.
 		///   </para>
 		///   <para>
-		///     Alternatively, if your program needs to control the main loop and needs to 
+		///     Alternatively, if your program needs to control the main loop and needs to
 		///     process events manually, you can invoke Begin to set things up manually and then
 		///     repeatedly call RunLoop with the wait parameter set to false.   By doing this
 		///     the RunLoop method will only process any pending events, timers, idle handlers and
@@ -2268,8 +2268,14 @@ namespace Terminal.Gui {
 			Current.Running = false;
 		}
 
+		/// <summary>
+		/// Invoked when the terminal was resized.
+		/// </summary>
+		static public Action OnResized;
+
 		static void TerminalResized ()
 		{
+			OnResized?.Invoke ();
 			var full = new Rect (0, 0, Driver.Cols, Driver.Rows);
 			Driver.Clip = full;
 			foreach (var t in toplevels) {