diff --git a/Example/demo.cs b/Example/demo.cs index a845c9088..da61d517b 100644 --- a/Example/demo.cs +++ b/Example/demo.cs @@ -237,12 +237,13 @@ static class Demo { // static void Editor () { - var tframe = Application.Top.Frame; - Application.Top.RemoveAll (); + Application.Init (); + var ntop = Application.Top; + var menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_File", new MenuItem [] { - new MenuItem ("_Close", "", () => { if (Quit ()) {Application.RequestStop (); } }), + new MenuItem ("_Close", "", () => { if (Quit ()) { running = MainApp; Application.RequestStop (); } }), }), new MenuBarItem ("_Edit", new MenuItem [] { new MenuItem ("_Copy", "", null), @@ -267,15 +268,13 @@ static class Demo { }; ntop.Add (win); - var text = new TextView (new Rect (0, 0, tframe.Width - 2, tframe.Height - 3)); + var text = new TextView () { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; if (fname != null) text.Text = System.IO.File.ReadAllText (fname); win.Add (text); Application.Run (ntop, false); - Application.Top.RemoveAll (); - Main (); } static bool Quit () @@ -450,20 +449,22 @@ static class Demo { static void ComboBoxDemo () { - IList items = new List (); - foreach (var dir in new [] { "/etc", @"\windows\System32" }) { + //TODO: Duplicated code in ListsAndCombos.cs Consider moving to shared assembly + var items = new List (); + foreach (var dir in new [] { "/etc", @$"{Environment.GetEnvironmentVariable ("SystemRoot")}\System32" }) { if (Directory.Exists (dir)) { items = Directory.GetFiles (dir).Union (Directory.GetDirectories (dir)) .Select (Path.GetFileName) .Where (x => char.IsLetterOrDigit (x [0])) - .OrderBy (x => x).ToList (); + .OrderBy (x => x).Select (x => ustring.Make (x)).ToList (); } } - var list = new ComboBox () { X = 0, Y = 0, Width = Dim.Fill(), Height = Dim.Fill() }; - list.SetSource(items.ToList()); - list.SelectedItemChanged += (object sender, ustring text) => { Application.RequestStop (); }; + var list = new ComboBox () { Width = Dim.Fill(), Height = Dim.Fill() }; + list.SetSource(items); + list.OpenSelectedItem += (ListViewItemEventArgs text) => { Application.RequestStop (); }; - var d = new Dialog ("Select source file", 40, 12) { list }; + var d = new Dialog () { Title = "Select source file", Width = Dim.Percent (50), Height = Dim.Percent (50) }; + d.Add (list); Application.Run (d); MessageBox.Query (60, 10, "Selected file", list.Text.ToString() == "" ? "Nothing selected" : list.Text.ToString(), "Ok"); @@ -534,11 +535,20 @@ static class Demo { } #endregion + public static Action running = MainApp; + static void Main () + { + while (running != null) { + running.Invoke (); + } + Application.Shutdown (); + } + public static Label ml; public static MenuBar menu; public static CheckBox menuKeysStyle; public static CheckBox menuAutoMouseNav; - static void Main () + static void MainApp () { if (Debugger.IsAttached) CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US"); @@ -578,14 +588,14 @@ static class Demo { menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_File", new MenuItem [] { - new MenuItem ("Text _Editor Demo", "", () => { Editor (); }), + new MenuItem ("Text _Editor Demo", "", () => { running = Editor; Application.RequestStop (); }), new MenuItem ("_New", "Creates new file", NewFile), new MenuItem ("_Open", "", Open), new MenuItem ("_Hex", "", () => ShowHex (top)), new MenuItem ("_Close", "", () => Close ()), new MenuItem ("_Disabled", "", () => { }, () => false), null, - new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; }) + new MenuItem ("_Quit", "", () => { if (Quit ()) { running = null; top.Running = false; } }) }), new MenuBarItem ("_Edit", new MenuItem [] { new MenuItem ("_Copy", "", Copy), @@ -648,9 +658,8 @@ static class Demo { new StatusItem(Key.F1, "~F1~ Help", () => Help()), new StatusItem(Key.F2, "~F2~ Load", Load), new StatusItem(Key.F3, "~F3~ Save", Save), - new StatusItem(Key.ControlQ, "~^Q~ Quit", () => { if (Quit ()) top.Running = false; }), - }) { - }; + new StatusItem(Key.ControlQ, "~^Q~ Quit", () => { if (Quit ()) { running = null; top.Running = false; } }) + }); win.Add (drag, dragText); @@ -671,7 +680,7 @@ static class Demo { top.Add (win); //top.Add (menu); top.Add (menu, statusBar); - Application.Run (); + Application.Run (top, false); } private static void Win_KeyPress (View.KeyEventEventArgs e) diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index d08effb53..3d663944d 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -589,16 +589,23 @@ namespace Terminal.Gui { MainLoop.MainIteration (); Iteration?.Invoke (); - } else if (wait == false) + } else if (wait == false) { return; - if (state.Toplevel.NeedDisplay != null && (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.childNeedsDisplay)) { + } + if (state.Toplevel != Top && (!Top.NeedDisplay.IsEmpty || Top.childNeedsDisplay)) { + Top.Redraw (Top.Bounds); + state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds); + } + if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.childNeedsDisplay) { state.Toplevel.Redraw (state.Toplevel.Bounds); - if (DebugDrawBounds) + if (DebugDrawBounds) { DrawBounds (state.Toplevel); + } state.Toplevel.PositionCursor (); Driver.Refresh (); - } else + } else { Driver.UpdateCursor (); + } } } diff --git a/Terminal.Gui/Core/Responder.cs b/Terminal.Gui/Core/Responder.cs index e796798f0..22b450a62 100644 --- a/Terminal.Gui/Core/Responder.cs +++ b/Terminal.Gui/Core/Responder.cs @@ -56,7 +56,7 @@ namespace Terminal.Gui { /// Gets or sets a value indicating whether this has focus. /// /// true if has focus; otherwise, false. - public virtual bool HasFocus { get; internal set; } + public virtual bool HasFocus { get; } // Key handling /// @@ -161,7 +161,6 @@ namespace Terminal.Gui { return false; } - /// /// Method invoked when a mouse event is generated /// @@ -195,8 +194,9 @@ namespace Terminal.Gui { /// /// Method invoked when a view gets focus. /// + /// The view that is losing focus. /// true, if the event was handled, false otherwise. - public virtual bool OnEnter () + public virtual bool OnEnter (View view) { return false; } @@ -204,8 +204,9 @@ namespace Terminal.Gui { /// /// Method invoked when a view loses focus. /// + /// The view that is getting focus. /// true, if the event was handled, false otherwise. - public virtual bool OnLeave () + public virtual bool OnLeave (View view) { return false; } diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index d2097964c..70407f3d2 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -124,6 +124,16 @@ namespace Terminal.Gui { TextFormatter textFormatter; + /// + /// Event fired when a subview is being added to this view. + /// + public Action Added; + + /// + /// Event fired when a subview is being removed from this view. + /// + public Action Removed; + /// /// Event fired when the view gets focus. /// @@ -552,6 +562,7 @@ namespace Terminal.Gui { subviews = new List (); subviews.Add (view); view.container = this; + OnAdded (view); if (view.CanFocus) CanFocus = true; SetNeedsLayout (); @@ -601,6 +612,7 @@ namespace Terminal.Gui { var touched = view.Frame; subviews.Remove (view); view.container = null; + OnRemoved (view); if (subviews.Count < 1) this.CanFocus = false; @@ -895,26 +907,31 @@ namespace Terminal.Gui { } } + bool hasFocus; /// public override bool HasFocus { get { - return base.HasFocus; + return hasFocus; } - internal set { - if (base.HasFocus != value) - if (value) - OnEnter (); - else - OnLeave (); - SetNeedsDisplay (); - base.HasFocus = value; + } - // Remove focus down the chain of subviews if focus is removed - if (!value && focused != null) { - focused.OnLeave (); - focused.HasFocus = false; - focused = null; - } + void SetHasFocus (bool value, View view) + { + if (hasFocus != value) { + hasFocus = value; + } + if (value) { + OnEnter (view); + } else { + OnLeave (view); + } + SetNeedsDisplay (); + + // Remove focus down the chain of subviews if focus is removed + if (!value && focused != null) { + focused.OnLeave (view); + focused.SetHasFocus (false, view); + focused = null; } } @@ -925,35 +942,58 @@ namespace Terminal.Gui { /// /// Constructs. /// - public FocusEventArgs () { } + /// The view that gets or loses focus. + public FocusEventArgs (View view) { View = view; } /// /// Indicates if the current focus event has already been processed and the driver should stop notifying any other event subscriber. /// Its important to set this value to true specially when updating any View's layout from inside the subscriber method. /// public bool Handled { get; set; } + /// + /// Indicates the current view that gets or loses focus. + /// + public View View { get; set; } + } + + /// + /// Method invoked when a subview is being added to this view. + /// + /// The subview being added. + public virtual void OnAdded (View view) + { + view.Added?.Invoke (this); + } + + /// + /// Method invoked when a subview is being removed from this view. + /// + /// The subview being removed. + public virtual void OnRemoved (View view) + { + view.Removed?.Invoke (this); } /// - public override bool OnEnter () + public override bool OnEnter (View view) { - FocusEventArgs args = new FocusEventArgs (); + FocusEventArgs args = new FocusEventArgs (view); Enter?.Invoke (args); if (args.Handled) return true; - if (base.OnEnter ()) + if (base.OnEnter (view)) return true; return false; } /// - public override bool OnLeave () + public override bool OnLeave (View view) { - FocusEventArgs args = new FocusEventArgs (); + FocusEventArgs args = new FocusEventArgs (view); Leave?.Invoke (args); if (args.Handled) return true; - if (base.OnLeave ()) + if (base.OnLeave (view)) return true; return false; @@ -1125,10 +1165,11 @@ namespace Terminal.Gui { throw new ArgumentException ("the specified view is not part of the hierarchy of this view"); if (focused != null) - focused.HasFocus = false; + focused.SetHasFocus (false, view); + var f = focused; focused = view; - focused.HasFocus = true; + focused.SetHasFocus (true, f); focused.EnsureFocus (); // Send focus upwards @@ -1322,7 +1363,7 @@ namespace Terminal.Gui { continue; } if (w.CanFocus && focused_idx != -1) { - focused.HasFocus = false; + focused.SetHasFocus (false, w); if (w != null && w.CanFocus) w.FocusLast (); @@ -1332,7 +1373,7 @@ namespace Terminal.Gui { } } if (focused != null) { - focused.HasFocus = false; + focused.SetHasFocus (false, this); focused = null; } return false; @@ -1364,7 +1405,7 @@ namespace Terminal.Gui { continue; } if (w.CanFocus && focused_idx != -1) { - focused.HasFocus = false; + focused.SetHasFocus (false, w); if (w != null && w.CanFocus) w.FocusFirst (); @@ -1374,7 +1415,7 @@ namespace Terminal.Gui { } } if (focused != null) { - focused.HasFocus = false; + focused.SetHasFocus (false, this); focused = null; } return false; diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index 5564c4010..b87d5ac42 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/Terminal.Gui/Views/ComboBox.cs @@ -4,15 +4,10 @@ // Authors: // Ross Ferguson (ross.c.ferguson@btinternet.com) // -// TODO: -// LayoutComplete() resize Height implement -// Cursor rolls of end of list when Height = Dim.Fill() and list fills frame -// using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using NStack; namespace Terminal.Gui { @@ -33,7 +28,12 @@ namespace Terminal.Gui { get => source; set { source = value; - SetNeedsDisplay (); + + // Only need to refresh list if its been added to a container view + if(SuperView != null && SuperView.Subviews.Contains(this)) { + Search_Changed (""); + SetNeedsDisplay (); + } } } @@ -46,34 +46,34 @@ namespace Terminal.Gui { /// public void SetSource (IList source) { - if (source == null) + if (source == null) { Source = null; - else { - Source = MakeWrapper (source); + } else { + listview.SetSource (source); + Source = listview.Source; } } /// - /// Changed event, raised when the selection has been confirmed. + /// This event is raised when the selected item in the has changed. /// - /// - /// Client code can hook up to this event, it is - /// raised when the selection has been confirmed. - /// - public event EventHandler SelectedItemChanged; + public Action SelectedItemChanged; + + /// + /// This event is raised when the user Double Clicks on an item or presses ENTER to open the selected item. + /// + public Action OpenSelectedItem; IList searchset; ustring text = ""; readonly TextField search; readonly ListView listview; - int height; - int width; bool autoHide = true; /// /// Public constructor /// - public ComboBox () : base() + public ComboBox () : base () { search = new TextField (""); listview = new ListView () { LayoutStyle = LayoutStyle.Computed, CanFocus = true }; @@ -81,6 +81,19 @@ namespace Terminal.Gui { Initialize (); } + /// + /// Public constructor + /// + /// + public ComboBox (ustring text) : base () + { + search = new TextField (""); + listview = new ListView () { LayoutStyle = LayoutStyle.Computed, CanFocus = true }; + + Initialize (); + Text = text; + } + /// /// Public constructor /// @@ -88,42 +101,45 @@ namespace Terminal.Gui { /// public ComboBox (Rect rect, IList source) : base (rect) { - SetSource (source); - this.height = rect.Height; - this.width = rect.Width; - - search = new TextField ("") { Width = width }; - listview = new ListView (rect, source) { LayoutStyle = LayoutStyle.Computed }; + search = new TextField ("") { Width = rect.Width }; + listview = new ListView (rect, source) { LayoutStyle = LayoutStyle.Computed, ColorScheme = Colors.Base }; Initialize (); + SetSource (source); } - static IListDataSource MakeWrapper (IList source) + private void Initialize () { - return new ListWrapper (source); - } - - private void Initialize() - { - ColorScheme = Colors.Base; - search.TextChanged += Search_Changed; + search.MouseClick += Search_MouseClick; + + listview.Y = Pos.Bottom (search); + listview.OpenSelectedItem += (ListViewItemEventArgs a) => Selected (); + + this.Add (listview, search); + this.SetFocus (search); // On resize LayoutComplete += (LayoutEventArgs a) => { - - search.Width = Bounds.Width; - listview.Width = autoHide ? Bounds.Width - 1 : Bounds.Width; + if (!autoHide && search.Frame.Width != Bounds.Width || + autoHide && search.Frame.Width != Bounds.Width - 1) { + search.Width = Bounds.Width; + listview.Width = listview.Width = autoHide ? Bounds.Width - 1 : Bounds.Width; + listview.Height = CalculatetHeight (); + search.SetRelativeLayout (Bounds); + listview.SetRelativeLayout (Bounds); + } }; listview.SelectedItemChanged += (ListViewItemEventArgs e) => { - if(searchset.Count > 0) - SetValue ((ustring)searchset [listview.SelectedItem]); + if (searchset.Count > 0) { + SetValue (searchset [listview.SelectedItem]); + } }; -#if false - Application.Loaded += (Application.ResizedEventArgs a) => { + Added += (View v) => { + // Determine if this view is hosted inside a dialog for (View view = this.SuperView; view != null; view = view.SuperView) { if (view is Dialog) { @@ -132,63 +148,60 @@ namespace Terminal.Gui { } } - ResetSearchSet (); - - ColorScheme = autoHide ? Colors.Base : ColorScheme = null; - - // Needs to be re-applied for LayoutStyle.Computed - // If Dim or Pos are null, these are the from the parametrized constructor - listview.Y = 1; - - if (Width == null) { - listview.Width = CalculateWidth (); - search.Width = width; - } else { - width = GetDimAsInt (Width, vertical: false); - search.Width = width; - listview.Width = CalculateWidth (); - } - - if (Height == null) { - var h = CalculatetHeight (); - listview.Height = h; - this.Height = h + 1; // adjust view to account for search box - } else { - if (height == 0) - height = GetDimAsInt (Height, vertical: true); - - listview.Height = CalculatetHeight (); - this.Height = height + 1; // adjust view to account for search box - } - - if (this.Text != null) - Search_Changed (Text); - - if (autoHide) - listview.ColorScheme = Colors.Menu; - else - search.ColorScheme = Colors.Menu; + SetNeedsLayout (); + SetNeedsDisplay (); + Search_Changed (Text); }; -#endif - search.MouseClick += Search_MouseClick; - - this.Add(listview, search); - this.SetFocus(search); } - private void Search_MouseClick (MouseEventArgs e) - { - if (e.MouseEvent.Flags != MouseFlags.Button1Clicked) - return; + /// + /// Gets the index of the currently selected item in the + /// + /// The selected item or -1 none selected. + public int SelectedItem { private set; get; } - SuperView.SetFocus (search); + bool isShow = false; + + /// + public new ColorScheme ColorScheme { + get { + return base.ColorScheme; + } + set { + listview.ColorScheme = value; + base.ColorScheme = value; + SetNeedsDisplay (); + } + } + + private void Search_MouseClick (MouseEventArgs me) + { + if (me.MouseEvent.X == Bounds.Right - 1 && me.MouseEvent.Y == Bounds.Top && me.MouseEvent.Flags == MouseFlags.Button1Pressed + && search.Text == "" && autoHide) { + + if (isShow) { + HideList (); + isShow = false; + } else { + // force deep copy + foreach (var item in Source.ToList()) { + searchset.Add (item); + } + + ShowList (); + isShow = true; + } + } else { + SuperView.SetFocus (search); + } } /// - public override bool OnEnter () + public override bool OnEnter (View view) { - if (!search.HasFocus) + if (!search.HasFocus) { this.SetFocus (search); + } search.CursorPosition = search.Text.RuneCount; @@ -203,46 +216,64 @@ namespace Terminal.Gui { { // Note: Cannot rely on "listview.SelectedItem != lastSelectedItem" because the list is dynamic. // So we cannot optimize. Ie: Don't call if not changed - SelectedItemChanged?.Invoke (this, search.Text); + SelectedItemChanged?.Invoke (new ListViewItemEventArgs(SelectedItem, search.Text)); + + return true; + } + + /// + /// Invokes the OnOpenSelectedItem event if it is defined. + /// + /// + public virtual bool OnOpenSelectedItem () + { + var value = search.Text; + OpenSelectedItem?.Invoke (new ListViewItemEventArgs (SelectedItem, value)); return true; } /// - public override bool ProcessKey(KeyEvent e) + public override void Redraw (Rect bounds) + { + base.Redraw (bounds); + + if (!autoHide) { + return; + } + + Move (Bounds.Right - 1, 0); + Driver.AddRune (Driver.DownArrow); + } + + /// + public override bool ProcessKey (KeyEvent e) { if (e.Key == Key.Tab) { - base.ProcessKey(e); + base.ProcessKey (e); return false; // allow tab-out to next control } + if(e.Key == Key.BackTab) { + base.ProcessKey (e); + this.FocusPrev (); + return false; // allow tab-out to prev control + } + if (e.Key == Key.Enter && listview.HasFocus) { - if (listview.Source.Count == 0 || searchset.Count == 0) { - text = ""; - return true; - } - - SetValue((ustring)searchset [listview.SelectedItem]); - search.CursorPosition = search.Text.RuneCount; - Search_Changed (search.Text); - OnSelectedChanged (); - - searchset.Clear(); - listview.Clear (); - listview.Height = 0; - this.SetFocus(search); - + Selected (); return true; } - if (e.Key == Key.CursorDown && search.HasFocus && listview.SelectedItem == 0 && searchset.Count > 0) { // jump to list + if (e.Key == Key.CursorDown && search.HasFocus && searchset.Count > 0) { // jump to list this.SetFocus (listview); - SetValue ((ustring)searchset [listview.SelectedItem]); + SetValue (searchset [listview.SelectedItem]); return true; } - if (e.Key == Key.CursorUp && search.HasFocus) // stop odd behavior on KeyUp when search has focus + if (e.Key == Key.CursorUp && search.HasFocus) { // stop odd behavior on KeyUp when search has focus return true; + } if (e.Key == Key.CursorUp && listview.HasFocus && listview.SelectedItem == 0 && searchset.Count > 0) // jump back to search { @@ -251,6 +282,34 @@ namespace Terminal.Gui { return true; } + if(e.Key == Key.PageDown) { + if (listview.SelectedItem != -1) { + listview.MovePageDown (); + } + return true; + } + + if (e.Key == Key.PageUp) { + if (listview.SelectedItem != -1) { + listview.MovePageUp (); + } + return true; + } + + if (e.Key == Key.Home) { + if (listview.SelectedItem != -1) { + listview.MoveHome (); + } + return true; + } + + if(e.Key == Key.End) { + if(listview.SelectedItem != -1) { + listview.MoveEnd (); + } + return true; + } + if (e.Key == Key.Esc) { this.SetFocus (search); search.Text = text = ""; @@ -259,22 +318,19 @@ namespace Terminal.Gui { } // Unix emulation - if (e.Key == Key.ControlU) - { - Reset(); + if (e.Key == Key.ControlU) { + Reset (); return true; } - return base.ProcessKey(e); + return base.ProcessKey (e); } /// /// The currently selected list item /// - public new ustring Text - { - get - { + public new ustring Text { + get { return text; } set { @@ -282,92 +338,131 @@ namespace Terminal.Gui { } } - private void SetValue(ustring text) + private void SetValue (object text) { search.TextChanged -= Search_Changed; - this.text = search.Text = text; + this.text = search.Text = text.ToString(); search.CursorPosition = 0; search.TextChanged += Search_Changed; + SelectedItem = GetSelectedItemFromSource (this.text); + OnSelectedChanged (); + } + + private void Selected () + { + if (listview.Source.Count == 0 || searchset.Count == 0) { + text = ""; + return; + } + + SetValue (searchset [listview.SelectedItem]); + search.CursorPosition = search.Text.RuneCount; + Search_Changed (search.Text); + OnOpenSelectedItem (); + Reset (keepSearchText: true); + } + + private int GetSelectedItemFromSource (ustring value) + { + if (source == null) { + return -1; + } + for (int i = 0; i < source.Count; i++) { + if (source.ToList () [i].ToString () == value) { + return i; + } + } + return -1; } /// /// Reset to full original list /// - private void Reset() + private void Reset (bool keepSearchText = false) { - search.Text = text = ""; - OnSelectedChanged(); + if (!keepSearchText) { + search.Text = text = ""; + } ResetSearchSet (); - listview.SetSource(searchset); - listview.Height = CalculatetHeight (); - - this.SetFocus(search); - } - - private void ResetSearchSet() - { - if (autoHide) { - if (searchset == null) - searchset = new List (); - else - searchset.Clear (); - } else - searchset = source.ToList (); - } - - private void Search_Changed (ustring text) - { - if (source == null) // Object initialization - return; - - if (ustring.IsNullOrEmpty (search.Text)) - ResetSearchSet (); - else - searchset = source.ToList().Cast().Where (x => x.StartsWith (search.Text)).ToList(); - listview.SetSource (searchset); listview.Height = CalculatetHeight (); - listview.Redraw (new Rect (0, 0, width, height)); // for any view behind this + this.SetFocus (search); + } + + private void ResetSearchSet (bool noCopy = false) + { + if (searchset == null) { + searchset = new List (); + } else { + searchset.Clear (); + } + + if (autoHide || noCopy) + return; + + // force deep copy + foreach (var item in Source.ToList ()) { + searchset.Add (item); + } + } + + private void Search_Changed (ustring text) + { + if (source == null) { // Object initialization + return; + } + + if (ustring.IsNullOrEmpty (search.Text)) { + ResetSearchSet (); + } else { + ResetSearchSet (noCopy: true); + + foreach (var item in source.ToList ()) { // Iterate to preserver object type and force deep copy + if (item.ToString().StartsWith (search.Text.ToString(), StringComparison.CurrentCultureIgnoreCase)) { + searchset.Add (item); + } + } + } + + ShowList (); + } + + /// + /// Show the search list + /// + /// + /// Consider making public + private void ShowList () + { + listview.SetSource (searchset); + listview.Clear (); // Ensure list shrinks in Dialog as you type + listview.Height = CalculatetHeight (); this.SuperView?.BringSubviewToFront (this); } + /// + /// Hide the search list + /// + /// + /// Consider making public + private void HideList () + { + Reset (); + } + /// /// Internal height of dynamic search list /// /// private int CalculatetHeight () { - return Math.Min (height, searchset.Count); - } + if (Bounds.Height == 0) + return 0; - /// - /// Internal width of search list - /// - /// - private int CalculateWidth () - { - return autoHide ? Math.Max (1, width - 1) : width; - } - - /// - /// Get Dim as integer value - /// - /// - /// - /// n - private int GetDimAsInt (Dim dim, bool vertical) - { - if (dim is Dim.DimAbsolute) - return dim.Anchor (0); - else { // Dim.Fill Dim.Factor - if(autoHide) - return vertical ? dim.Anchor (SuperView.Bounds.Height) : dim.Anchor (SuperView.Bounds.Width); - else - return vertical ? dim.Anchor (Bounds.Height) : dim.Anchor (Bounds.Width); - } + return Math.Min (Bounds.Height - 1, searchset?.Count ?? 0); } } } diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index b32d0f4dd..ade1510f7 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -589,15 +589,17 @@ namespace Terminal.Gui { /// public ListWrapper (IList source) { - count = source.Count; - marks = new BitArray (count); - this.src = source; + if (source != null) { + count = source.Count; + marks = new BitArray (count); + this.src = source; + } } /// /// Gets the number of items in the . /// - public int Count => src.Count; + public int Count => src != null ? src.Count : 0; void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width) { @@ -632,7 +634,7 @@ namespace Terminal.Gui { container.Move (col, line); var t = src [item]; if (t == null) { - RenderUstr (driver, ustring.Make(""), col, line, width); + RenderUstr (driver, ustring.Make (""), col, line, width); } else { if (t is ustring) { RenderUstr (driver, (ustring)t, col, line, width); diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 978252dfb..95a7af6ab 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -535,8 +535,6 @@ namespace Terminal.Gui { if (item == null || !item.IsEnabled ()) disabled = true; if (item != null && !disabled) current = me.Y - 1; - HasFocus = true; - SetNeedsDisplay (); CheckSubMenu (); return true; } diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index d0c7c6d60..0ab227cfe 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -93,14 +93,14 @@ namespace Terminal.Gui { } /// - public override bool OnLeave () + public override bool OnLeave (View view) { if (Application.mouseGrabView != null && Application.mouseGrabView == this) Application.UngrabMouse (); if (SelectedLength != 0 && !(Application.mouseGrabView is MenuBar)) ClearAllSelection (); - return base.OnLeave (); + return base.OnLeave (view); } /// diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index e86372073..475d6be0b 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -372,7 +372,7 @@ namespace UICatalog { // If the view supports a Source property, set it so we have something to look at if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType().GetProperty("Source").PropertyType == typeof(Terminal.Gui.IListDataSource)) { - var source = new ListWrapper (new List () { ustring.Make ("List Item #1"), ustring.Make ("List Item #2"), ustring.Make ("List Item #3")}); + var source = new ListWrapper (new List () { ustring.Make ("Test Text #1"), ustring.Make ("Test Text #2"), ustring.Make ("Test Text #3") }); view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source }); } diff --git a/UICatalog/Scenarios/ListsAndCombos.cs b/UICatalog/Scenarios/ListsAndCombos.cs index 19d10539c..7d44e8a89 100644 --- a/UICatalog/Scenarios/ListsAndCombos.cs +++ b/UICatalog/Scenarios/ListsAndCombos.cs @@ -12,13 +12,14 @@ namespace UICatalog.Scenarios { public override void Setup () { - List items = new List (); - foreach (var dir in new [] { "/etc", @"\windows\System32" }) { + //TODO: Duplicated code in Demo.cs Consider moving to shared assembly + var items = new List (); + foreach (var dir in new [] { "/etc", @$"{Environment.GetEnvironmentVariable ("SystemRoot")}\System32" }) { if (Directory.Exists (dir)) { items = Directory.GetFiles (dir).Union(Directory.GetDirectories(dir)) - .Select (Path.GetFileName) - .Where (x => char.IsLetterOrDigit (x [0])) - .OrderBy (x => x).ToList (); + .Select (Path.GetFileName) + .Where (x => char.IsLetterOrDigit (x [0])) + .OrderBy (x => x).Select(x => ustring.Make(x)).ToList() ; } } @@ -26,16 +27,16 @@ namespace UICatalog.Scenarios { var lbListView = new Label ("Listview") { ColorScheme = Colors.TopLevel, X = 0, - Width = 30 + Width = Dim.Percent (40) }; var listview = new ListView (items) { X = 0, Y = Pos.Bottom (lbListView) + 1, Height = Dim.Fill(2), - Width = 30 + Width = Dim.Percent (40) }; - listview.OpenSelectedItem += (ListViewItemEventArgs e) => lbListView.Text = items [listview.SelectedItem]; + listview.SelectedItemChanged += (ListViewItemEventArgs e) => lbListView.Text = items [listview.SelectedItem]; Win.Add (lbListView, listview); // ComboBox @@ -53,7 +54,7 @@ namespace UICatalog.Scenarios { }; comboBox.SetSource (items); - comboBox.SelectedItemChanged += (object sender, ustring text) => lbComboBox.Text = text; + comboBox.SelectedItemChanged += (ListViewItemEventArgs text) => lbComboBox.Text = items[comboBox.SelectedItem]; Win.Add (lbComboBox, comboBox); } } diff --git a/UnitTests/ResponderTests.cs b/UnitTests/ResponderTests.cs index 1e662a608..43e9cf186 100644 --- a/UnitTests/ResponderTests.cs +++ b/UnitTests/ResponderTests.cs @@ -32,8 +32,8 @@ namespace Terminal.Gui { Assert.False (r.MouseEvent (new MouseEvent () { Flags = MouseFlags.AllEvents })); Assert.False (r.OnMouseEnter (new MouseEvent () { Flags = MouseFlags.AllEvents })); Assert.False (r.OnMouseLeave (new MouseEvent () { Flags = MouseFlags.AllEvents })); - Assert.False (r.OnEnter ()); - Assert.False (r.OnLeave ()); + Assert.False (r.OnEnter (new View ())); + Assert.False (r.OnLeave (new View ())); } } } diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index 43473921b..353335915 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -102,8 +102,8 @@ namespace Terminal.Gui { Assert.False (r.MouseEvent (new MouseEvent () { Flags = MouseFlags.AllEvents })); Assert.False (r.OnMouseEnter (new MouseEvent () { Flags = MouseFlags.AllEvents })); Assert.False (r.OnMouseLeave (new MouseEvent () { Flags = MouseFlags.AllEvents })); - Assert.False (r.OnEnter ()); - Assert.False (r.OnLeave ()); + Assert.False (r.OnEnter (new View ())); + Assert.False (r.OnLeave (new View ())); // TODO: Add more } @@ -135,5 +135,26 @@ namespace Terminal.Gui { sub2.Width = Dim.Width (sub2); Assert.Throws (() => root.LayoutSubviews ()); } + + [Fact] + public void Added_Removing () + { + var v = new View (new Rect (0, 0, 10, 24)); + var t = new View (); + + v.Added += (View e) => { + Assert.True (v.SuperView == e); + }; + + v.Removed += (View e) => { + Assert.True (v.SuperView == null); + }; + + t.Add (v); + Assert.True (t.Subviews.Count == 1); + + t.Remove (v); + Assert.True (t.Subviews.Count == 0); + } } }