diff --git a/Designer/Program.cs b/Designer/Program.cs index d3dac91a4..e6a2bb67e 100644 --- a/Designer/Program.cs +++ b/Designer/Program.cs @@ -1,7 +1,12 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using Terminal.Gui; namespace Designer { +#if false class Surface : Window { public Surface () : base ("Designer") { @@ -37,9 +42,201 @@ namespace Designer { Height = Dim.Fill () }; - //Application.Top.Add (menu); - Application.Top.Add (login, password); + surface.Add (login, password); + Application.Top.Add (menu, surface); Application.Run (); } } +#elif false + class MainClass { + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("Repaint Issue") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + RadioGroup radioGroup = new RadioGroup (1, 1, new [] { "Short", "Longer Text --> Will not be repainted <--", "Short" }); + + Button replaceButtonLonger = new Button (1, 10, "Replace Texts above Longer") { + Clicked = () => { radioGroup.RadioLabels = new string [] { "Longer than before", "Shorter Text", "Longer than before" }; } + }; + + Button replaceButtonSmaller = new Button (35, 10, "Replace Texts above Smaller") { + Clicked = () => { radioGroup.RadioLabels = new string [] { "Short", "Longer Text --> Will not be repainted <--", "Short" }; } + }; + + window.Add (radioGroup, replaceButtonLonger, replaceButtonSmaller); + Application.Top.Add (window); + Application.Run (); + } + } +#elif false + class MainClass { + public static void Main (string [] args) + { + + string[] radioLabels = { "First", "Second" }; + Application.Init(); + + Window window = new Window("Redraw issue when setting coordinates of label") { X = 0, Y = 0, Width = Dim.Fill(), Height = Dim.Fill() }; + + Label radioLabel = new Label("Radio selection: ") { X = 1, Y = 1 }; + Label otherLabel = new Label("Other label: ") { X = Pos.Left(radioLabel), Y = Pos.Top(radioLabel) + radioLabels.Length }; + + RadioGroup radioGroup = new RadioGroup(radioLabels) { X = Pos.Right(radioLabel), Y = Pos.Top(radioLabel) }; + RadioGroup radioGroup2 = new RadioGroup(new[] { "Option 1 of the second radio group", "Option 2 of the second radio group" }) { X = Pos.Right(radioLabel), Y = Pos.Top(otherLabel) }; + + Button replaceButton = new Button(1, 10, "Add radio labels") { + Clicked = () => + { + radioGroup.RadioLabels = new[] { "First", "Second", "Third <- Third ->", "Fourth <- Fourth ->" }; + otherLabel.Y = Pos.Top(radioLabel) + radioGroup.RadioLabels.Length; + //Application.Refresh(); // Even this won't redraw the app correctly, only a terminal resize will re-render the view. + //typeof(Application).GetMethod("TerminalResized", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).Invoke(null, null); + } + }; + + window.Add(radioLabel, otherLabel, radioGroup, radioGroup2, replaceButton); + Application.Top.Add(window); + Application.Run(); + } + } +#elif false + class MainClass { + static TaskScheduler syncContextTaskScheduler; + + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("When awaiting a method it is awaited until the cursor moves") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + + Button button = new Button (1, 1, "Load Items"); + ListView itemsList = new ListView (); + itemsList.X = Pos.X (button); + itemsList.Y = Pos.Y (button) + 4; + button.Clicked += async () => { + Application.MainLoop.Invoke (async () => { + itemsList.Clear (); + //When the button is 'clicked' the following is executed + Debug.WriteLine ($"Clicked the button"); + var items = await LoadItemsAsync (); + + //However the following line is not executed + //until the button is clicked again or + //until the cursor is moved to the next view/control + Debug.WriteLine ($"Got {items.Count} items)"); + itemsList.SetSource (items); + + //Without calling this the UI is not updated + //this.LayoutSubviews (); + }); + }; + + window.Add (itemsList, button); + Application.Top.Add (window); + Application.Run (); + } + + private static Task> LoadItemsAsync () + { + try { + // Do something that takes lot of times. + List items = new List () { "One", "Two", "Three" }; + return Task.FromResult (items); + } catch (TaskCanceledException ex) { + Debug.WriteLine (ex.Message); + return Task.FromResult (new List ()); + } + } + } +#elif false + class MainClass { + static TaskScheduler syncContextTaskScheduler; + + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("When awaiting a method it is awaited until the cursor moves") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + + Button button = new Button (1, 1, "Load Items"); + ListView itemsList = new ListView (); + itemsList.X = Pos.X (button); + itemsList.Y = Pos.Y (button) + 4; + button.Clicked += async () => { + itemsList.Clear (); + //When the button is 'clicked' the following is executed + Debug.WriteLine ($"Clicked the button"); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ()) { + if (button.Text == "Cancel") { + button.Text = "Load Items"; + cancellationTokenSource.Cancel (); + } else + button.Text = "Cancel"; + syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext (); + await Task>.Run (() => LoadItemsAsync (cancellationTokenSource.Token).Result).ContinueWith (task => { + //However the following line is not executed + //until the button is clicked again or + //until the cursor is moved to the next view/control + var items = task.Result; + Debug.WriteLine ($"Got {items.Count} items)"); + itemsList.SetSource (items); + + //Without calling this the UI is not updated + //this.LayoutSubviews (); + button.Text = "Load Items"; + }, CancellationToken.None, + TaskContinuationOptions.OnlyOnRanToCompletion, + syncContextTaskScheduler); + } + }; + + window.Add (itemsList, button); + Application.Top.Add (window); + Application.Run (); + } + + private static Task> LoadItemsAsync (CancellationToken cancellationToken) + { + try { + // Do something that takes lot of times. + //Task.Delay (3000); + Thread.Sleep (800); + if (cancellationToken.IsCancellationRequested) + throw new TaskCanceledException ("Task was cancelled"); + + List items = new List () { "One", "Two", "Three" }; + cancellationToken.ThrowIfCancellationRequested (); + return Task.FromResult (items); + } catch (TaskCanceledException ex) { + Debug.WriteLine (ex.Message); + return Task.FromResult (new List ()); + } + } + } +#elif true + class MainClass { + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("Button repainting issue when changing the text length to one smaller than before.") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + string strLonger = "Button with a long text"; + Button button = new Button (1, 5, strLonger); + button.Clicked += () => { + //When the button is 'clicked' the following is executed + Debug.WriteLine ($"Clicked the button with text '{button.Text}'"); + if (button.Text != strLonger) { + button.Text = strLonger; + } else + button.Text = "Small text"; + }; + + window.Add (button); + Application.Top.Add (window); + Application.Run (); + } + } + +#endif } diff --git a/Example/Example.csproj b/Example/Example.csproj index 3f6523667..95808f859 100644 --- a/Example/Example.csproj +++ b/Example/Example.csproj @@ -1,4 +1,4 @@ - + Debug @@ -52,4 +52,4 @@ - + \ No newline at end of file diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index bc222f3a2..e2cb24d79 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -430,7 +430,7 @@ namespace Terminal.Gui { SetNeedsDisplay (Bounds); } - bool layoutNeeded = true; + internal bool layoutNeeded = true; internal void SetNeedsLayout () { @@ -2057,11 +2057,28 @@ namespace Terminal.Gui { DrawBounds (state.Toplevel); state.Toplevel.PositionCursor (); Driver.Refresh (); + } else if (CheckLayoutNeeded (state.Toplevel)) { + TerminalResized (); + layoutNeeded = false; } else Driver.UpdateCursor (); } } + static bool layoutNeeded; + static bool CheckLayoutNeeded (View view) + { + if (view.layoutNeeded) + return layoutNeeded = view.layoutNeeded; + + for (int i = 0; view.Subviews.Count > i; i++) { + CheckLayoutNeeded (view.Subviews [i]); + if (layoutNeeded) + return layoutNeeded; + } + return layoutNeeded; + } + internal static bool DebugDrawBounds; // Need to look into why this does not work properly. diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 7304000e6..c11bdbe40 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -68,10 +68,16 @@ namespace Terminal.Gui { CanFocus = true; this.IsDefault = is_default; Text = text; + int w = SetWidthHeight (text, is_default); + Frame = new Rect (0, 0, w, 1); + } + + private int SetWidthHeight (ustring text, bool is_default) + { int w = text.Length + 4 + (is_default ? 2 : 0); Width = w; Height = 1; - Frame = new Rect (0, 0, w, 1); + return w; } /// @@ -96,6 +102,10 @@ namespace Terminal.Gui { } set { + if (text?.Length != value?.Length) { + SetWidthHeight (value, is_default); + SetNeedsLayout (); + } text = value; Update (); } @@ -166,8 +176,7 @@ namespace Terminal.Gui { { if (Char.ToUpper ((char)key.KeyValue) == hot_key) { this.SuperView.SetFocus (this); - if (Clicked != null) - Clicked (); + Clicked?.Invoke (); return true; } return false;