diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index ca48e21d3..8b0422173 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/Terminal.Gui/Views/ComboBox.cs @@ -24,8 +24,6 @@ public class ComboBox : View { public ComboListView (ComboBox container, bool hideDropdownListOnClick) => SetInitialProperties (container, hideDropdownListOnClick); - public ComboListView (ComboBox container, Rect rect, IList source, bool hideDropdownListOnClick) : base (rect, source) => SetInitialProperties (container, hideDropdownListOnClick); - public ComboListView (ComboBox container, IList source, bool hideDropdownListOnClick) : base (source) => SetInitialProperties (container, hideDropdownListOnClick); void SetInitialProperties (ComboBox container, bool hideDropdownListOnClick) diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 5e57efbe9..b4bb18c42 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -1,932 +1,906 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Text; -namespace Terminal.Gui { +namespace Terminal.Gui; +/// +/// Implement to provide custom rendering for a . +/// +public interface IListDataSource { /// - /// Implement to provide custom rendering for a . + /// Returns the number of elements to display /// - public interface IListDataSource { - /// - /// Returns the number of elements to display - /// - int Count { get; } + int Count { get; } - /// - /// Returns the maximum length of elements to display - /// - int Length { get; } + /// + /// Returns the maximum length of elements to display + /// + int Length { get; } - /// - /// This method is invoked to render a specified item, the method should cover the entire provided width. - /// - /// The render. - /// The list view to render. - /// The console driver to render. - /// Describes whether the item being rendered is currently selected by the user. - /// The index of the item to render, zero for the first item and so on. - /// The column where the rendering will start - /// The line where the rendering will be done. - /// The width that must be filled out. - /// The index of the string to be displayed. - /// - /// The default color will be set before this method is invoked, and will be based on whether the item is selected or not. - /// - void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0); + /// + /// This method is invoked to render a specified item, the method should cover the entire provided width. + /// + /// The render. + /// The list view to render. + /// The console driver to render. + /// Describes whether the item being rendered is currently selected by the user. + /// The index of the item to render, zero for the first item and so on. + /// The column where the rendering will start + /// The line where the rendering will be done. + /// The width that must be filled out. + /// The index of the string to be displayed. + /// + /// The default color will be set before this method is invoked, and will be based on whether the item is selected or not. + /// + void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0); - /// - /// Should return whether the specified item is currently marked. - /// - /// , if marked, otherwise. - /// Item index. - bool IsMarked (int item); + /// + /// Should return whether the specified item is currently marked. + /// + /// , if marked, otherwise. + /// Item index. + bool IsMarked (int item); - /// - /// Flags the item as marked. - /// - /// Item index. - /// If set to value. - void SetMark (int item, bool value); + /// + /// Flags the item as marked. + /// + /// Item index. + /// If set to value. + void SetMark (int item, bool value); - /// - /// Return the source as IList. - /// - /// - IList ToList (); + /// + /// Return the source as IList. + /// + /// + IList ToList (); +} + +/// +/// ListView renders a scrollable list of data where each item can be activated to perform an action. +/// +/// +/// +/// The displays lists of data and allows the user to scroll through the data. +/// Items in the can be activated firing an event (with the ENTER key or a mouse double-click). +/// If the property is true, elements of the list can be marked by the user. +/// +/// +/// By default uses to render the items of any +/// object (e.g. arrays, , +/// and other collections). Alternatively, an object that implements +/// can be provided giving full control of what is rendered. +/// +/// +/// can display any object that implements the interface. +/// values are converted into values before rendering, and other values are +/// converted into by calling and then converting to . +/// +/// +/// To change the contents of the ListView, set the property (when +/// providing custom rendering via ) or call +/// an is being used. +/// +/// +/// When is set to true the rendering will prefix the rendered items with +/// [x] or [ ] and bind the SPACE key to toggle the selection. To implement a different +/// marking style set to false and implement custom rendering. +/// +/// +/// Searching the ListView with the keyboard is supported. Users type the +/// first characters of an item, and the first item that starts with what the user types will be selected. +/// +/// +public class ListView : View { + int _top, _left; + int _selected = -1; + + IListDataSource _source; + /// + /// Gets or sets the backing this , enabling custom rendering. + /// + /// The source. + /// + /// Use to set a new source. + /// + public IListDataSource Source { + get => _source; + set { + _source = value; + KeystrokeNavigator.Collection = _source?.ToList (); + _top = 0; + _selected = -1; + _lastSelectedItem = -1; + SetNeedsDisplay (); + } } /// - /// ListView renders a scrollable list of data where each item can be activated to perform an action. + /// Sets the source of the to an . /// + /// An object implementing the IList interface. /// - /// - /// The displays lists of data and allows the user to scroll through the data. - /// Items in the can be activated firing an event (with the ENTER key or a mouse double-click). - /// If the property is true, elements of the list can be marked by the user. - /// - /// - /// By default uses to render the items of any - /// object (e.g. arrays, , - /// and other collections). Alternatively, an object that implements - /// can be provided giving full control of what is rendered. - /// - /// - /// can display any object that implements the interface. - /// values are converted into values before rendering, and other values are - /// converted into by calling and then converting to . - /// - /// - /// To change the contents of the ListView, set the property (when - /// providing custom rendering via ) or call - /// an is being used. - /// - /// - /// When is set to true the rendering will prefix the rendered items with - /// [x] or [ ] and bind the SPACE key to toggle the selection. To implement a different - /// marking style set to false and implement custom rendering. - /// - /// - /// Searching the ListView with the keyboard is supported. Users type the - /// first characters of an item, and the first item that starts with what the user types will be selected. - /// + /// Use the property to set a new source and use custome rendering. /// - public class ListView : View { - int top, left; - int selected = -1; - - IListDataSource source; - /// - /// Gets or sets the backing this , enabling custom rendering. - /// - /// The source. - /// - /// Use to set a new source. - /// - public IListDataSource Source { - get => source; - set { - source = value; - KeystrokeNavigator.Collection = source?.ToList (); - top = 0; - selected = -1; - lastSelectedItem = -1; - SetNeedsDisplay (); - } + public void SetSource (IList source) + { + if (source == null && (Source == null || !(Source is ListWrapper))) + Source = null; + else { + Source = MakeWrapper (source); } + } - /// - /// Sets the source of the to an . - /// - /// An object implementing the IList interface. - /// - /// Use the property to set a new source and use custome rendering. - /// - public void SetSource (IList source) - { + /// + /// Sets the source to an value asynchronously. + /// + /// An item implementing the IList interface. + /// + /// Use the property to set a new source and use custom rendering. + /// + public Task SetSourceAsync (IList source) + { + return Task.Factory.StartNew (() => { if (source == null && (Source == null || !(Source is ListWrapper))) Source = null; - else { + else Source = MakeWrapper (source); + return source; + }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + } + + bool _allowsMarking; + /// + /// Gets or sets whether this allows items to be marked. + /// + /// Set to to allow marking elements of the list. + /// + /// If set to , will render items marked items with "[x]", and unmarked items with "[ ]" + /// spaces. SPACE key will toggle marking. The default is . + /// + public bool AllowsMarking { + get => _allowsMarking; + set { + _allowsMarking = value; + if (_allowsMarking) { + KeyBindings.Add (KeyCode.Space, Command.ToggleChecked); + } else { + KeyBindings.Remove (KeyCode.Space); } + + SetNeedsDisplay (); } + } - /// - /// Sets the source to an value asynchronously. - /// - /// An item implementing the IList interface. - /// - /// Use the property to set a new source and use custom rendering. - /// - public Task SetSourceAsync (IList source) - { - return Task.Factory.StartNew (() => { - if (source == null && (Source == null || !(Source is ListWrapper))) - Source = null; - else - Source = MakeWrapper (source); - return source; - }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); - } - - bool allowsMarking; - /// - /// Gets or sets whether this allows items to be marked. - /// - /// Set to to allow marking elements of the list. - /// - /// If set to , will render items marked items with "[x]", and unmarked items with "[ ]" - /// spaces. SPACE key will toggle marking. The default is . - /// - public bool AllowsMarking { - get => allowsMarking; - set { - allowsMarking = value; - if (allowsMarking) { - KeyBindings.Add (KeyCode.Space, Command.ToggleChecked); - } else { - KeyBindings.Remove (KeyCode.Space); - } - - SetNeedsDisplay (); - } - } - - /// - /// If set to more than one item can be selected. If selecting - /// an item will cause all others to be un-selected. The default is . - /// - public bool AllowsMultipleSelection { - get => allowsMultipleSelection; - set { - allowsMultipleSelection = value; - if (Source != null && !allowsMultipleSelection) { - // Clear all selections except selected - for (int i = 0; i < Source.Count; i++) { - if (Source.IsMarked (i) && i != selected) { - Source.SetMark (i, false); - } + /// + /// If set to more than one item can be selected. If selecting + /// an item will cause all others to be un-selected. The default is . + /// + public bool AllowsMultipleSelection { + get => _allowsMultipleSelection; + set { + _allowsMultipleSelection = value; + if (Source != null && !_allowsMultipleSelection) { + // Clear all selections except selected + for (int i = 0; i < Source.Count; i++) { + if (Source.IsMarked (i) && i != _selected) { + Source.SetMark (i, false); } } + } + SetNeedsDisplay (); + } + } + + /// + /// Gets or sets the item that is displayed at the top of the . + /// + /// The top item. + public int TopItem { + get => _top; + set { + if (_source == null) + return; + + if (value < 0 || (_source.Count > 0 && value >= _source.Count)) + throw new ArgumentException ("value"); + _top = Math.Max (value, 0); + SetNeedsDisplay (); + } + } + + /// + /// Gets or sets the leftmost column that is currently visible (when scrolling horizontally). + /// + /// The left position. + public int LeftItem { + get => _left; + set { + if (_source == null) + return; + + if (value < 0 || (Maxlength > 0 && value >= Maxlength)) + throw new ArgumentException ("value"); + _left = value; + SetNeedsDisplay (); + } + } + + /// + /// Gets the widest item in the list. + /// + public int Maxlength => (_source?.Length) ?? 0; + + /// + /// Gets or sets the index of the currently selected item. + /// + /// The selected item. + public int SelectedItem { + get => _selected; + set { + if (_source == null || _source.Count == 0) { + return; + } + if (value < -1 || value >= _source.Count) { + throw new ArgumentException ("value"); + } + _selected = value; + OnSelectedChanged (); + } + } + + static IListDataSource MakeWrapper (IList source) + { + return new ListWrapper (source); + } + + /// + /// Initializes a new instance of . Set the property to display something. + /// + public ListView () + { + SetInitialProperties (); + } + + /// + /// Initializes a new instance of that will display the provided data source, using relative positioning. + /// + /// object that provides a mechanism to render the data. + /// The number of elements on the collection should not change, if you must change, set + /// the "Source" property to reset the internal settings of the ListView. + public ListView (IListDataSource source) + { + this._source = source; + SetInitialProperties (); + } + + /// + /// Initializes a new instance of that will display the contents of the object + /// implementing the interface with an absolute position. + /// + /// An IList data source, if the elements of the IList are strings, + /// the string is rendered, otherwise the ToString() method is invoked on the result. + public ListView (IList source) : this (MakeWrapper (source)) + { + SetInitialProperties (); + } + + void SetInitialProperties () + { + Source = _source; + CanFocus = true; + + // Things this view knows how to do + AddCommand (Command.LineUp, () => MoveUp ()); + AddCommand (Command.LineDown, () => MoveDown ()); + AddCommand (Command.ScrollUp, () => ScrollUp (1)); + AddCommand (Command.ScrollDown, () => ScrollDown (1)); + AddCommand (Command.PageUp, () => MovePageUp ()); + AddCommand (Command.PageDown, () => MovePageDown ()); + AddCommand (Command.TopHome, () => MoveHome ()); + AddCommand (Command.BottomEnd, () => MoveEnd ()); + AddCommand (Command.OpenSelectedItem, () => OnOpenSelectedItem ()); + AddCommand (Command.ToggleChecked, () => MarkUnmarkRow ()); + + // Default keybindings for all ListViews + KeyBindings.Add (KeyCode.CursorUp, Command.LineUp); + KeyBindings.Add (KeyCode.P | KeyCode.CtrlMask, Command.LineUp); + + KeyBindings.Add (KeyCode.CursorDown, Command.LineDown); + KeyBindings.Add (KeyCode.N | KeyCode.CtrlMask, Command.LineDown); + + KeyBindings.Add (KeyCode.PageUp, Command.PageUp); + + KeyBindings.Add (KeyCode.PageDown, Command.PageDown); + KeyBindings.Add (KeyCode.V | KeyCode.CtrlMask, Command.PageDown); + + KeyBindings.Add (KeyCode.Home, Command.TopHome); + + KeyBindings.Add (KeyCode.End, Command.BottomEnd); + + KeyBindings.Add (KeyCode.Enter, Command.OpenSelectedItem); + } + + /// + public override void OnDrawContent (Rect contentArea) + { + base.OnDrawContent (contentArea); + + var current = ColorScheme.Focus; + Driver.SetAttribute (current); + Move (0, 0); + var f = Bounds; + var item = _top; + bool focused = HasFocus; + int col = _allowsMarking ? 2 : 0; + int start = _left; + + for (int row = 0; row < f.Height; row++, item++) { + bool isSelected = item == _selected; + + var newcolor = focused ? (isSelected ? ColorScheme.Focus : GetNormalColor ()) + : (isSelected ? ColorScheme.HotNormal : GetNormalColor ()); + + if (newcolor != current) { + Driver.SetAttribute (newcolor); + current = newcolor; + } + + Move (0, row); + if (_source == null || item >= _source.Count) { + for (int c = 0; c < f.Width; c++) + Driver.AddRune ((Rune)' '); + } else { + var rowEventArgs = new ListViewRowEventArgs (item); + OnRowRender (rowEventArgs); + if (rowEventArgs.RowAttribute != null && current != rowEventArgs.RowAttribute) { + current = (Attribute)rowEventArgs.RowAttribute; + Driver.SetAttribute (current); + } + if (_allowsMarking) { + Driver.AddRune (_source.IsMarked (item) ? (AllowsMultipleSelection ? CM.Glyphs.Checked : CM.Glyphs.Selected) : + (AllowsMultipleSelection ? CM.Glyphs.UnChecked : CM.Glyphs.UnSelected)); + Driver.AddRune ((Rune)' '); + } + Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start); + } + } + } + + /// + /// This event is raised when the selected item in the has changed. + /// + public event EventHandler SelectedItemChanged; + + /// + /// This event is raised when the user Double Clicks on an item or presses ENTER to open the selected item. + /// + public event EventHandler OpenSelectedItem; + + /// + /// This event is invoked when this is being drawn before rendering. + /// + public event EventHandler RowRender; + + /// + /// Gets the that searches the collection as + /// the user types. + /// + public CollectionNavigator KeystrokeNavigator { get; private set; } = new CollectionNavigator (); + + /// + public override bool OnProcessKeyDown (Key a) + { + // Enable user to find & select an item by typing text + if (CollectionNavigator.IsCompatibleKey (a)) { + var newItem = KeystrokeNavigator?.GetNextMatchingItem (SelectedItem, (char)a); + if (newItem is int && newItem != -1) { + SelectedItem = (int)newItem; + EnsureSelectedItemVisible (); SetNeedsDisplay (); + return true; } } + return false; + } - /// - /// Gets or sets the item that is displayed at the top of the . - /// - /// The top item. - public int TopItem { - get => top; - set { - if (source == null) - return; - - if (value < 0 || (source.Count > 0 && value >= source.Count)) - throw new ArgumentException ("value"); - top = Math.Max (value, 0); - SetNeedsDisplay (); - } - } - - /// - /// Gets or sets the leftmost column that is currently visible (when scrolling horizontally). - /// - /// The left position. - public int LeftItem { - get => left; - set { - if (source == null) - return; - - if (value < 0 || (Maxlength > 0 && value >= Maxlength)) - throw new ArgumentException ("value"); - left = value; - SetNeedsDisplay (); - } - } - - /// - /// Gets the widest item in the list. - /// - public int Maxlength => (source?.Length) ?? 0; - - /// - /// Gets or sets the index of the currently selected item. - /// - /// The selected item. - public int SelectedItem { - get => selected; - set { - if (source == null || source.Count == 0) { - return; - } - if (value < -1 || value >= source.Count) { - throw new ArgumentException ("value"); - } - selected = value; - OnSelectedChanged (); - } - } - - static IListDataSource MakeWrapper (IList source) - { - return new ListWrapper (source); - } - - /// - /// Initializes a new instance of that will display the - /// contents of the object implementing the interface, - /// with relative positioning. - /// - /// An data source, if the elements are strings or ustrings, - /// the string is rendered, otherwise the ToString() method is invoked on the result. - public ListView (IList source) : this (MakeWrapper (source)) - { - } - - /// - /// Initializes a new instance of that will display the provided data source, using relative positioning. - /// - /// object that provides a mechanism to render the data. - /// The number of elements on the collection should not change, if you must change, set - /// the "Source" property to reset the internal settings of the ListView. - public ListView (IListDataSource source) : base () - { - this.source = source; - Initialize (); - } - - /// - /// Initializes a new instance of . Set the property to display something. - /// - public ListView () : base () - { - Initialize (); - } - - /// - /// Initializes a new instance of that will display the contents of the object implementing the interface with an absolute position. - /// - /// Frame for the listview. - /// An IList data source, if the elements of the IList are strings or ustrings, - /// the string is rendered, otherwise the ToString() method is invoked on the result. - public ListView (Rect rect, IList source) : this (rect, MakeWrapper (source)) - { - Initialize (); - } - - /// - /// Initializes a new instance of with the provided data source and an absolute position - /// - /// Frame for the listview. - /// IListDataSource object that provides a mechanism to render the data. - /// The number of elements on the collection should not change, if you must change, - /// set the "Source" property to reset the internal settings of the ListView. - public ListView (Rect rect, IListDataSource source) : base (rect) - { - this.source = source; - Initialize (); - } - - void Initialize () - { - Source = source; - CanFocus = true; - - // Things this view knows how to do - AddCommand (Command.LineUp, () => MoveUp ()); - AddCommand (Command.LineDown, () => MoveDown ()); - AddCommand (Command.ScrollUp, () => ScrollUp (1)); - AddCommand (Command.ScrollDown, () => ScrollDown (1)); - AddCommand (Command.PageUp, () => MovePageUp ()); - AddCommand (Command.PageDown, () => MovePageDown ()); - AddCommand (Command.TopHome, () => MoveHome ()); - AddCommand (Command.BottomEnd, () => MoveEnd ()); - AddCommand (Command.OpenSelectedItem, () => OnOpenSelectedItem ()); - AddCommand (Command.ToggleChecked, () => MarkUnmarkRow ()); - - // Default keybindings for all ListViews - KeyBindings.Add (KeyCode.CursorUp, Command.LineUp); - KeyBindings.Add (KeyCode.P | KeyCode.CtrlMask, Command.LineUp); - - KeyBindings.Add (KeyCode.CursorDown, Command.LineDown); - KeyBindings.Add (KeyCode.N | KeyCode.CtrlMask, Command.LineDown); - - KeyBindings.Add (KeyCode.PageUp, Command.PageUp); - - KeyBindings.Add (KeyCode.PageDown, Command.PageDown); - KeyBindings.Add (KeyCode.V | KeyCode.CtrlMask, Command.PageDown); - - KeyBindings.Add (KeyCode.Home, Command.TopHome); - - KeyBindings.Add (KeyCode.End, Command.BottomEnd); - - KeyBindings.Add (KeyCode.Enter, Command.OpenSelectedItem); - } - - /// - public override void OnDrawContent (Rect contentArea) - { - base.OnDrawContent (contentArea); - - var current = ColorScheme.Focus; - Driver.SetAttribute (current); - Move (0, 0); - var f = Bounds; - var item = top; - bool focused = HasFocus; - int col = allowsMarking ? 2 : 0; - int start = left; - - for (int row = 0; row < f.Height; row++, item++) { - bool isSelected = item == selected; - - var newcolor = focused ? (isSelected ? ColorScheme.Focus : GetNormalColor ()) - : (isSelected ? ColorScheme.HotNormal : GetNormalColor ()); - - if (newcolor != current) { - Driver.SetAttribute (newcolor); - current = newcolor; - } - - Move (0, row); - if (source == null || item >= source.Count) { - for (int c = 0; c < f.Width; c++) - Driver.AddRune ((Rune)' '); - } else { - var rowEventArgs = new ListViewRowEventArgs (item); - OnRowRender (rowEventArgs); - if (rowEventArgs.RowAttribute != null && current != rowEventArgs.RowAttribute) { - current = (Attribute)rowEventArgs.RowAttribute; - Driver.SetAttribute (current); - } - if (allowsMarking) { - Driver.AddRune (source.IsMarked (item) ? (AllowsMultipleSelection ? CM.Glyphs.Checked : CM.Glyphs.Selected) : - (AllowsMultipleSelection ? CM.Glyphs.UnChecked : CM.Glyphs.UnSelected)); - Driver.AddRune ((Rune)' '); - } - Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start); - } - } - } - - /// - /// This event is raised when the selected item in the has changed. - /// - public event EventHandler SelectedItemChanged; - - /// - /// This event is raised when the user Double Clicks on an item or presses ENTER to open the selected item. - /// - public event EventHandler OpenSelectedItem; - - /// - /// This event is invoked when this is being drawn before rendering. - /// - public event EventHandler RowRender; - - /// - /// Gets the that searches the collection as - /// the user types. - /// - public CollectionNavigator KeystrokeNavigator { get; private set; } = new CollectionNavigator (); - - /// - public override bool OnProcessKeyDown (Key a) - { - // Enable user to find & select an item by typing text - if (CollectionNavigator.IsCompatibleKey (a)) { - var newItem = KeystrokeNavigator?.GetNextMatchingItem (SelectedItem, (char)a); - if (newItem is int && newItem != -1) { - SelectedItem = (int)newItem; - EnsureSelectedItemVisible (); - SetNeedsDisplay (); + /// + /// If and are both , + /// unmarks all marked items other than the currently selected. + /// + /// if unmarking was successful. + public virtual bool AllowsAll () + { + if (!_allowsMarking) + return false; + if (!AllowsMultipleSelection) { + for (int i = 0; i < Source.Count; i++) { + if (Source.IsMarked (i) && i != _selected) { + Source.SetMark (i, false); return true; } } - return false; - } - - /// - /// If and are both , - /// unmarks all marked items other than the currently selected. - /// - /// if unmarking was successful. - public virtual bool AllowsAll () - { - if (!allowsMarking) - return false; - if (!AllowsMultipleSelection) { - for (int i = 0; i < Source.Count; i++) { - if (Source.IsMarked (i) && i != selected) { - Source.SetMark (i, false); - return true; - } - } - } - return true; - } - - /// - /// Marks the if it is not already marked. - /// - /// if the was marked. - public virtual bool MarkUnmarkRow () - { - if (AllowsAll ()) { - Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem)); - SetNeedsDisplay (); - return true; - } - - return false; - } - - /// - /// Changes the to the item at the top of the visible list. - /// - /// - public virtual bool MovePageUp () - { - int n = (selected - Bounds.Height); - if (n < 0) - n = 0; - if (n != selected) { - selected = n; - top = Math.Max (selected, 0); - OnSelectedChanged (); - SetNeedsDisplay (); - } - - return true; - } - - /// - /// Changes the to the item just below the bottom - /// of the visible list, scrolling if needed. - /// - /// - public virtual bool MovePageDown () - { - var n = (selected + Bounds.Height); - if (n >= source.Count) - n = source.Count - 1; - if (n != selected) { - selected = n; - if (source.Count >= Bounds.Height) - top = Math.Max (selected, 0); - else - top = 0; - OnSelectedChanged (); - SetNeedsDisplay (); - } - - return true; - } - - /// - /// Changes the to the next item in the list, - /// scrolling the list if needed. - /// - /// - public virtual bool MoveDown () - { - if (source.Count == 0) { - // Do we set lastSelectedItem to -1 here? - return false; //Nothing for us to move to - } - if (selected >= source.Count) { - // If for some reason we are currently outside of the - // valid values range, we should select the bottommost valid value. - // This can occur if the backing data source changes. - selected = source.Count - 1; - OnSelectedChanged (); - SetNeedsDisplay (); - } else if (selected + 1 < source.Count) { //can move by down by one. - selected++; - - if (selected >= top + Bounds.Height) { - top++; - } else if (selected < top) { - top = Math.Max (selected, 0); - } - OnSelectedChanged (); - SetNeedsDisplay (); - } else if (selected == 0) { - OnSelectedChanged (); - SetNeedsDisplay (); - } else if (selected >= top + Bounds.Height) { - top = Math.Max (source.Count - Bounds.Height, 0); - SetNeedsDisplay (); - } - - return true; - } - - /// - /// Changes the to the previous item in the list, - /// scrolling the list if needed. - /// - /// - public virtual bool MoveUp () - { - if (source.Count == 0) { - // Do we set lastSelectedItem to -1 here? - return false; //Nothing for us to move to - } - if (selected >= source.Count) { - // If for some reason we are currently outside of the - // valid values range, we should select the bottommost valid value. - // This can occur if the backing data source changes. - selected = source.Count - 1; - OnSelectedChanged (); - SetNeedsDisplay (); - } else if (selected > 0) { - selected--; - if (selected > Source.Count) { - selected = Source.Count - 1; - } - if (selected < top) { - top = Math.Max (selected, 0); - } else if (selected > top + Bounds.Height) { - top = Math.Max (selected - Bounds.Height + 1, 0); - } - OnSelectedChanged (); - SetNeedsDisplay (); - } else if (selected < top) { - top = Math.Max (selected, 0); - SetNeedsDisplay (); - } - return true; - } - - /// - /// Changes the to last item in the list, - /// scrolling the list if needed. - /// - /// - public virtual bool MoveEnd () - { - if (source.Count > 0 && selected != source.Count - 1) { - selected = source.Count - 1; - if (top + selected > Bounds.Height - 1) { - top = Math.Max (selected, 0); - } - OnSelectedChanged (); - SetNeedsDisplay (); - } - - return true; - } - - /// - /// Changes the to the first item in the list, - /// scrolling the list if needed. - /// - /// - public virtual bool MoveHome () - { - if (selected != 0) { - selected = 0; - top = Math.Max (selected, 0); - OnSelectedChanged (); - SetNeedsDisplay (); - } - - return true; - } - - /// - /// Scrolls the view down by items. - /// - /// Number of items to scroll down. - public virtual bool ScrollDown (int items) - { - top = Math.Max (Math.Min (top + items, source.Count - 1), 0); - SetNeedsDisplay (); - return true; - } - - /// - /// Scrolls the view up by items. - /// - /// Number of items to scroll up. - public virtual bool ScrollUp (int items) - { - top = Math.Max (top - items, 0); - SetNeedsDisplay (); - return true; - } - - /// - /// Scrolls the view right. - /// - /// Number of columns to scroll right. - public virtual bool ScrollRight (int cols) - { - left = Math.Max (Math.Min (left + cols, Maxlength - 1), 0); - SetNeedsDisplay (); - return true; - } - - /// - /// Scrolls the view left. - /// - /// Number of columns to scroll left. - public virtual bool ScrollLeft (int cols) - { - left = Math.Max (left - cols, 0); - SetNeedsDisplay (); - return true; - } - - int lastSelectedItem = -1; - private bool allowsMultipleSelection = true; - - /// - /// Invokes the event if it is defined. - /// - /// - public virtual bool OnSelectedChanged () - { - if (selected != lastSelectedItem) { - var value = source?.Count > 0 ? source.ToList () [selected] : null; - SelectedItemChanged?.Invoke (this, new ListViewItemEventArgs (selected, value)); - lastSelectedItem = selected; - EnsureSelectedItemVisible (); - return true; - } - - return false; - } - - /// - /// Invokes the event if it is defined. - /// - /// - public virtual bool OnOpenSelectedItem () - { - if (source.Count <= selected || selected < 0 || OpenSelectedItem == null) { - return false; - } - - var value = source.ToList () [selected]; - - OpenSelectedItem?.Invoke (this, new ListViewItemEventArgs (selected, value)); - - return true; - } - - /// - /// Virtual method that will invoke the . - /// - /// - public virtual void OnRowRender (ListViewRowEventArgs rowEventArgs) - { - RowRender?.Invoke (this, rowEventArgs); - } - - /// - public override bool OnEnter (View view) - { - if (IsInitialized) { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - } - if (lastSelectedItem != selected) { - EnsureSelectedItemVisible (); - } - - return base.OnEnter (view); - } - - /// - /// Ensures the selected item is always visible on the screen. - /// - public void EnsureSelectedItemVisible () - { - if (SuperView?.IsInitialized == true) { - if (selected < top) { - top = Math.Max (selected, 0); - } else if (Bounds.Height > 0 && selected >= top + Bounds.Height) { - top = Math.Max (selected - Bounds.Height + 1, 0); - } - LayoutStarted -= ListView_LayoutStarted; - } else { - LayoutStarted += ListView_LayoutStarted; - } - } - - private void ListView_LayoutStarted (object sender, LayoutEventArgs e) - { - EnsureSelectedItemVisible (); - } - - /// - public override void PositionCursor () - { - if (allowsMarking) - Move (0, selected - top); - else - Move (Bounds.Width - 1, selected - top); - } - - /// - public override bool MouseEvent (MouseEvent me) - { - if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) && - me.Flags != MouseFlags.WheeledDown && me.Flags != MouseFlags.WheeledUp && - me.Flags != MouseFlags.WheeledRight && me.Flags != MouseFlags.WheeledLeft) - return false; - - if (!HasFocus && CanFocus) { - SetFocus (); - } - - if (source == null) { - return false; - } - - if (me.Flags == MouseFlags.WheeledDown) { - ScrollDown (1); - return true; - } else if (me.Flags == MouseFlags.WheeledUp) { - ScrollUp (1); - return true; - } else if (me.Flags == MouseFlags.WheeledRight) { - ScrollRight (1); - return true; - } else if (me.Flags == MouseFlags.WheeledLeft) { - ScrollLeft (1); - return true; - } - - if (me.Y + top >= source.Count - || me.Y + top < 0 - || me.Y + top > top + Bounds.Height) { - return true; - } - - selected = top + me.Y; - if (AllowsAll ()) { - Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem)); - SetNeedsDisplay (); - return true; - } - OnSelectedChanged (); - SetNeedsDisplay (); - if (me.Flags == MouseFlags.Button1DoubleClicked) { - OnOpenSelectedItem (); - } - - return true; } + return true; } /// - /// Provides a default implementation of that renders - /// items using . + /// Marks the if it is not already marked. /// - public class ListWrapper : IListDataSource { - IList src; - BitArray marks; - int count, len; - - /// - public ListWrapper (IList source) - { - if (source != null) { - count = source.Count; - marks = new BitArray (count); - src = source; - len = GetMaxLengthItem (); - } + /// if the was marked. + public virtual bool MarkUnmarkRow () + { + if (AllowsAll ()) { + Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem)); + SetNeedsDisplay (); + return true; } - /// - public int Count => src != null ? src.Count : 0; + return false; + } - /// - public int Length => len; - - int GetMaxLengthItem () - { - if (src == null || src?.Count == 0) { - return 0; - } - - int maxLength = 0; - for (int i = 0; i < src.Count; i++) { - var t = src [i]; - int l; - if (t is string u) { - l = u.GetColumns (); - } else if (t is string s) { - l = s.Length; - } else { - l = t.ToString ().Length; - } - - if (l > maxLength) { - maxLength = l; - } - } - - return maxLength; + /// + /// Changes the to the item at the top of the visible list. + /// + /// + public virtual bool MovePageUp () + { + int n = (_selected - Bounds.Height); + if (n < 0) + n = 0; + if (n != _selected) { + _selected = n; + _top = Math.Max (_selected, 0); + OnSelectedChanged (); + SetNeedsDisplay (); } - void RenderUstr (ConsoleDriver driver, string ustr, int col, int line, int width, int start = 0) - { - var u = TextFormatter.ClipAndJustify (ustr, width, TextAlignment.Left); - driver.AddStr (u); - width -= u.GetColumns (); - while (width-- > 0) { - driver.AddRune ((Rune)' '); - } + return true; + } + + /// + /// Changes the to the item just below the bottom + /// of the visible list, scrolling if needed. + /// + /// + public virtual bool MovePageDown () + { + var n = (_selected + Bounds.Height); + if (n >= _source.Count) + n = _source.Count - 1; + if (n != _selected) { + _selected = n; + if (_source.Count >= Bounds.Height) + _top = Math.Max (_selected, 0); + else + _top = 0; + OnSelectedChanged (); + SetNeedsDisplay (); } - /// - public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width, int start = 0) - { - container.Move (col, line); - var t = src? [item]; - if (t == null) { - RenderUstr (driver, "", col, line, width); - } else { - if (t is string u) { - RenderUstr (driver, u, col, line, width, start); - } else if (t is string s) { - RenderUstr (driver, s, col, line, width, start); - } else { - RenderUstr (driver, t.ToString (), col, line, width, start); - } + return true; + } + + /// + /// Changes the to the next item in the list, + /// scrolling the list if needed. + /// + /// + public virtual bool MoveDown () + { + if (_source.Count == 0) { + // Do we set lastSelectedItem to -1 here? + return false; //Nothing for us to move to + } + if (_selected >= _source.Count) { + // If for some reason we are currently outside of the + // valid values range, we should select the bottommost valid value. + // This can occur if the backing data source changes. + _selected = _source.Count - 1; + OnSelectedChanged (); + SetNeedsDisplay (); + } else if (_selected + 1 < _source.Count) { //can move by down by one. + _selected++; + + if (_selected >= _top + Bounds.Height) { + _top++; + } else if (_selected < _top) { + _top = Math.Max (_selected, 0); } + OnSelectedChanged (); + SetNeedsDisplay (); + } else if (_selected == 0) { + OnSelectedChanged (); + SetNeedsDisplay (); + } else if (_selected >= _top + Bounds.Height) { + _top = Math.Max (_source.Count - Bounds.Height, 0); + SetNeedsDisplay (); } - /// - public bool IsMarked (int item) - { - if (item >= 0 && item < count) - return marks [item]; + return true; + } + + /// + /// Changes the to the previous item in the list, + /// scrolling the list if needed. + /// + /// + public virtual bool MoveUp () + { + if (_source.Count == 0) { + // Do we set lastSelectedItem to -1 here? + return false; //Nothing for us to move to + } + if (_selected >= _source.Count) { + // If for some reason we are currently outside of the + // valid values range, we should select the bottommost valid value. + // This can occur if the backing data source changes. + _selected = _source.Count - 1; + OnSelectedChanged (); + SetNeedsDisplay (); + } else if (_selected > 0) { + _selected--; + if (_selected > Source.Count) { + _selected = Source.Count - 1; + } + if (_selected < _top) { + _top = Math.Max (_selected, 0); + } else if (_selected > _top + Bounds.Height) { + _top = Math.Max (_selected - Bounds.Height + 1, 0); + } + OnSelectedChanged (); + SetNeedsDisplay (); + } else if (_selected < _top) { + _top = Math.Max (_selected, 0); + SetNeedsDisplay (); + } + return true; + } + + /// + /// Changes the to last item in the list, + /// scrolling the list if needed. + /// + /// + public virtual bool MoveEnd () + { + if (_source.Count > 0 && _selected != _source.Count - 1) { + _selected = _source.Count - 1; + if (_top + _selected > Bounds.Height - 1) { + _top = Math.Max (_selected, 0); + } + OnSelectedChanged (); + SetNeedsDisplay (); + } + + return true; + } + + /// + /// Changes the to the first item in the list, + /// scrolling the list if needed. + /// + /// + public virtual bool MoveHome () + { + if (_selected != 0) { + _selected = 0; + _top = Math.Max (_selected, 0); + OnSelectedChanged (); + SetNeedsDisplay (); + } + + return true; + } + + /// + /// Scrolls the view down by items. + /// + /// Number of items to scroll down. + public virtual bool ScrollDown (int items) + { + _top = Math.Max (Math.Min (_top + items, _source.Count - 1), 0); + SetNeedsDisplay (); + return true; + } + + /// + /// Scrolls the view up by items. + /// + /// Number of items to scroll up. + public virtual bool ScrollUp (int items) + { + _top = Math.Max (_top - items, 0); + SetNeedsDisplay (); + return true; + } + + /// + /// Scrolls the view right. + /// + /// Number of columns to scroll right. + public virtual bool ScrollRight (int cols) + { + _left = Math.Max (Math.Min (_left + cols, Maxlength - 1), 0); + SetNeedsDisplay (); + return true; + } + + /// + /// Scrolls the view left. + /// + /// Number of columns to scroll left. + public virtual bool ScrollLeft (int cols) + { + _left = Math.Max (_left - cols, 0); + SetNeedsDisplay (); + return true; + } + + int _lastSelectedItem = -1; + private bool _allowsMultipleSelection = true; + + /// + /// Invokes the event if it is defined. + /// + /// + public virtual bool OnSelectedChanged () + { + if (_selected != _lastSelectedItem) { + var value = _source?.Count > 0 ? _source.ToList () [_selected] : null; + SelectedItemChanged?.Invoke (this, new ListViewItemEventArgs (_selected, value)); + _lastSelectedItem = _selected; + EnsureSelectedItemVisible (); + return true; + } + + return false; + } + + /// + /// Invokes the event if it is defined. + /// + /// + public virtual bool OnOpenSelectedItem () + { + if (_source.Count <= _selected || _selected < 0 || OpenSelectedItem == null) { return false; } - /// - public void SetMark (int item, bool value) - { - if (item >= 0 && item < count) - marks [item] = value; + var value = _source.ToList () [_selected]; + + OpenSelectedItem?.Invoke (this, new ListViewItemEventArgs (_selected, value)); + + return true; + } + + /// + /// Virtual method that will invoke the . + /// + /// + public virtual void OnRowRender (ListViewRowEventArgs rowEventArgs) + { + RowRender?.Invoke (this, rowEventArgs); + } + + /// + public override bool OnEnter (View view) + { + if (IsInitialized) { + Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); + } + if (_lastSelectedItem != _selected) { + EnsureSelectedItemVisible (); } - /// - public IList ToList () - { - return src; - } + return base.OnEnter (view); + } - /// - public int StartsWith (string search) - { - if (src == null || src?.Count == 0) { - return -1; + /// + /// Ensures the selected item is always visible on the screen. + /// + public void EnsureSelectedItemVisible () + { + if (SuperView?.IsInitialized == true) { + if (_selected < _top) { + _top = Math.Max (_selected, 0); + } else if (Bounds.Height > 0 && _selected >= _top + Bounds.Height) { + _top = Math.Max (_selected - Bounds.Height + 1, 0); } - - for (int i = 0; i < src.Count; i++) { - var t = src [i]; - if (t is string u) { - if (u.ToUpper ().StartsWith (search.ToUpperInvariant ())) { - return i; - } - } else if (t is string s) { - if (s.StartsWith (search, StringComparison.InvariantCultureIgnoreCase)) { - return i; - } - } - } - return -1; + LayoutStarted -= ListView_LayoutStarted; + } else { + LayoutStarted += ListView_LayoutStarted; } } + + private void ListView_LayoutStarted (object sender, LayoutEventArgs e) + { + EnsureSelectedItemVisible (); + } + + /// + public override void PositionCursor () + { + if (_allowsMarking) + Move (0, _selected - _top); + else + Move (Bounds.Width - 1, _selected - _top); + } + + /// + public override bool MouseEvent (MouseEvent me) + { + if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) && + me.Flags != MouseFlags.WheeledDown && me.Flags != MouseFlags.WheeledUp && + me.Flags != MouseFlags.WheeledRight && me.Flags != MouseFlags.WheeledLeft) + return false; + + if (!HasFocus && CanFocus) { + SetFocus (); + } + + if (_source == null) { + return false; + } + + if (me.Flags == MouseFlags.WheeledDown) { + ScrollDown (1); + return true; + } else if (me.Flags == MouseFlags.WheeledUp) { + ScrollUp (1); + return true; + } else if (me.Flags == MouseFlags.WheeledRight) { + ScrollRight (1); + return true; + } else if (me.Flags == MouseFlags.WheeledLeft) { + ScrollLeft (1); + return true; + } + + if (me.Y + _top >= _source.Count + || me.Y + _top < 0 + || me.Y + _top > _top + Bounds.Height) { + return true; + } + + _selected = _top + me.Y; + if (AllowsAll ()) { + Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem)); + SetNeedsDisplay (); + return true; + } + OnSelectedChanged (); + SetNeedsDisplay (); + if (me.Flags == MouseFlags.Button1DoubleClicked) { + OnOpenSelectedItem (); + } + + return true; + } +} + +/// +/// Provides a default implementation of that renders +/// items using . +/// +public class ListWrapper : IListDataSource { + IList src; + BitArray marks; + int count, len; + + /// + public ListWrapper (IList source) + { + if (source != null) { + count = source.Count; + marks = new BitArray (count); + src = source; + len = GetMaxLengthItem (); + } + } + + /// + public int Count => src != null ? src.Count : 0; + + /// + public int Length => len; + + int GetMaxLengthItem () + { + if (src == null || src?.Count == 0) { + return 0; + } + + int maxLength = 0; + for (int i = 0; i < src.Count; i++) { + var t = src [i]; + int l; + if (t is string u) { + l = u.GetColumns (); + } else if (t is string s) { + l = s.Length; + } else { + l = t.ToString ().Length; + } + + if (l > maxLength) { + maxLength = l; + } + } + + return maxLength; + } + + void RenderUstr (ConsoleDriver driver, string ustr, int col, int line, int width, int start = 0) + { + var u = TextFormatter.ClipAndJustify (ustr, width, TextAlignment.Left); + driver.AddStr (u); + width -= u.GetColumns (); + while (width-- > 0) { + driver.AddRune ((Rune)' '); + } + } + + /// + public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width, int start = 0) + { + container.Move (col, line); + var t = src? [item]; + if (t == null) { + RenderUstr (driver, "", col, line, width); + } else { + if (t is string u) { + RenderUstr (driver, u, col, line, width, start); + } else if (t is string s) { + RenderUstr (driver, s, col, line, width, start); + } else { + RenderUstr (driver, t.ToString (), col, line, width, start); + } + } + } + + /// + public bool IsMarked (int item) + { + if (item >= 0 && item < count) + return marks [item]; + return false; + } + + /// + public void SetMark (int item, bool value) + { + if (item >= 0 && item < count) + marks [item] = value; + } + + /// + public IList ToList () + { + return src; + } + + /// + public int StartsWith (string search) + { + if (src == null || src?.Count == 0) { + return -1; + } + + for (int i = 0; i < src.Count; i++) { + var t = src [i]; + if (t is string u) { + if (u.ToUpper ().StartsWith (search.ToUpperInvariant ())) { + return i; + } + } else if (t is string s) { + if (s.StartsWith (search, StringComparison.InvariantCultureIgnoreCase)) { + return i; + } + } + } + return -1; + } } diff --git a/UnitTests/Views/ListViewTests.cs b/UnitTests/Views/ListViewTests.cs index a2fb00e38..18f0284e2 100644 --- a/UnitTests/Views/ListViewTests.cs +++ b/UnitTests/Views/ListViewTests.cs @@ -1,269 +1,266 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; -namespace Terminal.Gui.ViewsTests { - public class ListViewTests { - readonly ITestOutputHelper output; +namespace Terminal.Gui.ViewsTests; +public class ListViewTests { + readonly ITestOutputHelper _output; - public ListViewTests (ITestOutputHelper output) + public ListViewTests (ITestOutputHelper output) + { + this._output = output; + } + + [Fact] + public void Constructors_Defaults () + { + var lv = new ListView (); + Assert.Null (lv.Source); + Assert.True (lv.CanFocus); + Assert.Equal (-1, lv.SelectedItem); + + lv = new ListView (new List () { "One", "Two", "Three" }); + Assert.NotNull (lv.Source); + Assert.Equal (-1, lv.SelectedItem); + + lv = new ListView (new NewListDataSource ()); + Assert.NotNull (lv.Source); + Assert.Equal (-1, lv.SelectedItem); + + lv = new ListView (new List () { "One", "Two", "Three" }) { Y = 1, Width = 10, Height = 20 }; + Assert.NotNull (lv.Source); + Assert.Equal (-1, lv.SelectedItem); + Assert.Equal (new Rect (0, 1, 10, 20), lv.Frame); + + lv = new ListView (new NewListDataSource ()) { Y = 1, Width = 10, Height = 20 }; + Assert.NotNull (lv.Source); + Assert.Equal (-1, lv.SelectedItem); + Assert.Equal (new Rect (0, 1, 10, 20), lv.Frame); + } + + [Fact] + public void ListViewSelectThenDown () + { + var lv = new ListView (new List () { "One", "Two", "Three" }); + lv.AllowsMarking = true; + + Assert.NotNull (lv.Source); + + // first item should be deselected by default + Assert.Equal (-1, lv.SelectedItem); + + // nothing is ticked + Assert.False (lv.Source.IsMarked (0)); + Assert.False (lv.Source.IsMarked (1)); + Assert.False (lv.Source.IsMarked (2)); + + lv.KeyBindings.Add (KeyCode.Space | KeyCode.ShiftMask, Command.ToggleChecked, Command.LineDown); + + var ev = new Key (KeyCode.Space | KeyCode.ShiftMask); + + // view should indicate that it has accepted and consumed the event + Assert.True (lv.NewKeyDownEvent (ev)); + + // first item should now be selected + Assert.Equal (0, lv.SelectedItem); + + // none of the items should be ticked + Assert.False (lv.Source.IsMarked (0)); + Assert.False (lv.Source.IsMarked (1)); + Assert.False (lv.Source.IsMarked (2)); + + // Press key combo again + Assert.True (lv.NewKeyDownEvent (ev)); + + // second item should now be selected + Assert.Equal (1, lv.SelectedItem); + + // first item only should be ticked + Assert.True (lv.Source.IsMarked (0)); + Assert.False (lv.Source.IsMarked (1)); + Assert.False (lv.Source.IsMarked (2)); + + // Press key combo again + Assert.True (lv.NewKeyDownEvent (ev)); + Assert.Equal (2, lv.SelectedItem); + Assert.True (lv.Source.IsMarked (0)); + Assert.True (lv.Source.IsMarked (1)); + Assert.False (lv.Source.IsMarked (2)); + + // Press key combo again + Assert.True (lv.NewKeyDownEvent (ev)); + Assert.Equal (2, lv.SelectedItem); // cannot move down any further + Assert.True (lv.Source.IsMarked (0)); + Assert.True (lv.Source.IsMarked (1)); + Assert.True (lv.Source.IsMarked (2)); // but can toggle marked + + // Press key combo again + Assert.True (lv.NewKeyDownEvent (ev)); + Assert.Equal (2, lv.SelectedItem); // cannot move down any further + Assert.True (lv.Source.IsMarked (0)); + Assert.True (lv.Source.IsMarked (1)); + Assert.False (lv.Source.IsMarked (2)); // untoggle toggle marked + } + [Fact] + public void SettingEmptyKeybindingThrows () + { + var lv = new ListView (new List () { "One", "Two", "Three" }); + Assert.Throws (() => lv.KeyBindings.Add (KeyCode.Space)); + } + + /// + /// Tests that when none of the Commands in a chained keybinding are possible + /// the returns the appropriate result + /// + [Fact] + public void ListViewProcessKeyReturnValue_WithMultipleCommands () + { + var lv = new ListView (new List () { "One", "Two", "Three", "Four" }); + + Assert.NotNull (lv.Source); + + // first item should be deselected by default + Assert.Equal (-1, lv.SelectedItem); + + // bind shift down to move down twice in control + lv.KeyBindings.Add (KeyCode.CursorDown | KeyCode.ShiftMask, Command.LineDown, Command.LineDown); + + var ev = new Key (KeyCode.CursorDown | KeyCode.ShiftMask); + + Assert.True (lv.NewKeyDownEvent (ev), "The first time we move down 2 it should be possible"); + + // After moving down twice from -1 we should be at 'Two' + Assert.Equal (1, lv.SelectedItem); + + // clear the items + lv.SetSource (null); + + // Press key combo again - return should be false this time as none of the Commands are allowable + Assert.False (lv.NewKeyDownEvent (ev), "We cannot move down so will not respond to this"); + } + + private class NewListDataSource : IListDataSource { + public int Count => throw new NotImplementedException (); + + public int Length => throw new NotImplementedException (); + + public bool IsMarked (int item) { - this.output = output; + throw new NotImplementedException (); } - [Fact] - public void Constructors_Defaults () + public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0) { - var lv = new ListView (); - Assert.Null (lv.Source); - Assert.True (lv.CanFocus); - Assert.Equal (-1, lv.SelectedItem); - - lv = new ListView (new List () { "One", "Two", "Three" }); - Assert.NotNull (lv.Source); - Assert.Equal (-1, lv.SelectedItem); - - lv = new ListView (new NewListDataSource ()); - Assert.NotNull (lv.Source); - Assert.Equal (-1, lv.SelectedItem); - - lv = new ListView (new Rect (0, 1, 10, 20), new List () { "One", "Two", "Three" }); - Assert.NotNull (lv.Source); - Assert.Equal (-1, lv.SelectedItem); - Assert.Equal (new Rect (0, 1, 10, 20), lv.Frame); - - lv = new ListView (new Rect (0, 1, 10, 20), new NewListDataSource ()); - Assert.NotNull (lv.Source); - Assert.Equal (-1, lv.SelectedItem); - Assert.Equal (new Rect (0, 1, 10, 20), lv.Frame); + throw new NotImplementedException (); } - [Fact] - public void ListViewSelectThenDown () + public void SetMark (int item, bool value) { - var lv = new ListView (new List () { "One", "Two", "Three" }); - lv.AllowsMarking = true; - - Assert.NotNull (lv.Source); - - // first item should be deselected by default - Assert.Equal (-1, lv.SelectedItem); - - // nothing is ticked - Assert.False (lv.Source.IsMarked (0)); - Assert.False (lv.Source.IsMarked (1)); - Assert.False (lv.Source.IsMarked (2)); - - lv.KeyBindings.Add (KeyCode.Space | KeyCode.ShiftMask, Command.ToggleChecked, Command.LineDown); - - var ev = new Key (KeyCode.Space | KeyCode.ShiftMask); - - // view should indicate that it has accepted and consumed the event - Assert.True (lv.NewKeyDownEvent (ev)); - - // first item should now be selected - Assert.Equal (0, lv.SelectedItem); - - // none of the items should be ticked - Assert.False (lv.Source.IsMarked (0)); - Assert.False (lv.Source.IsMarked (1)); - Assert.False (lv.Source.IsMarked (2)); - - // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); - - // second item should now be selected - Assert.Equal (1, lv.SelectedItem); - - // first item only should be ticked - Assert.True (lv.Source.IsMarked (0)); - Assert.False (lv.Source.IsMarked (1)); - Assert.False (lv.Source.IsMarked (2)); - - // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); - Assert.Equal (2, lv.SelectedItem); - Assert.True (lv.Source.IsMarked (0)); - Assert.True (lv.Source.IsMarked (1)); - Assert.False (lv.Source.IsMarked (2)); - - // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); - Assert.Equal (2, lv.SelectedItem); // cannot move down any further - Assert.True (lv.Source.IsMarked (0)); - Assert.True (lv.Source.IsMarked (1)); - Assert.True (lv.Source.IsMarked (2)); // but can toggle marked - - // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); - Assert.Equal (2, lv.SelectedItem); // cannot move down any further - Assert.True (lv.Source.IsMarked (0)); - Assert.True (lv.Source.IsMarked (1)); - Assert.False (lv.Source.IsMarked (2)); // untoggle toggle marked - } - [Fact] - public void SettingEmptyKeybindingThrows () - { - var lv = new ListView (new List () { "One", "Two", "Three" }); - Assert.Throws (() => lv.KeyBindings.Add (KeyCode.Space)); + throw new NotImplementedException (); } - /// - /// Tests that when none of the Commands in a chained keybinding are possible - /// the returns the appropriate result - /// - [Fact] - public void ListViewProcessKeyReturnValue_WithMultipleCommands () + public IList ToList () { - var lv = new ListView (new List () { "One", "Two", "Three", "Four" }); - - Assert.NotNull (lv.Source); - - // first item should be deselected by default - Assert.Equal (-1, lv.SelectedItem); - - // bind shift down to move down twice in control - lv.KeyBindings.Add (KeyCode.CursorDown | KeyCode.ShiftMask, Command.LineDown, Command.LineDown); - - var ev = new Key (KeyCode.CursorDown | KeyCode.ShiftMask); - - Assert.True (lv.NewKeyDownEvent (ev), "The first time we move down 2 it should be possible"); - - // After moving down twice from -1 we should be at 'Two' - Assert.Equal (1, lv.SelectedItem); - - // clear the items - lv.SetSource (null); - - // Press key combo again - return should be false this time as none of the Commands are allowable - Assert.False (lv.NewKeyDownEvent (ev), "We cannot move down so will not respond to this"); + return new List () { "One", "Two", "Three" }; } + } - private class NewListDataSource : IListDataSource { - public int Count => throw new NotImplementedException (); + [Fact] + public void KeyBindings_Command () + { + List source = new List () { "One", "Two", "Three" }; + ListView lv = new ListView (source) { Height = 2, AllowsMarking = true }; + lv.BeginInit (); lv.EndInit (); + Assert.Equal (-1, lv.SelectedItem); + Assert.True (lv.NewKeyDownEvent (new (KeyCode.CursorDown))); + Assert.Equal (0, lv.SelectedItem); + Assert.True (lv.NewKeyDownEvent (new (KeyCode.CursorUp))); + Assert.Equal (0, lv.SelectedItem); + Assert.True (lv.NewKeyDownEvent (new (KeyCode.PageDown))); + Assert.Equal (2, lv.SelectedItem); + Assert.Equal (2, lv.TopItem); + Assert.True (lv.NewKeyDownEvent (new (KeyCode.PageUp))); + Assert.Equal (0, lv.SelectedItem); + Assert.Equal (0, lv.TopItem); + Assert.False (lv.Source.IsMarked (lv.SelectedItem)); + Assert.True (lv.NewKeyDownEvent (new (KeyCode.Space))); + Assert.True (lv.Source.IsMarked (lv.SelectedItem)); + var opened = false; + lv.OpenSelectedItem += (s, _) => opened = true; + Assert.True (lv.NewKeyDownEvent (new (KeyCode.Enter))); + Assert.True (opened); + Assert.True (lv.NewKeyDownEvent (new (KeyCode.End))); + Assert.Equal (2, lv.SelectedItem); + Assert.True (lv.NewKeyDownEvent (new (KeyCode.Home))); + Assert.Equal (0, lv.SelectedItem); + } - public int Length => throw new NotImplementedException (); + [Fact] + [AutoInitShutdown] + public void RowRender_Event () + { + var rendered = false; + var source = new List () { "one", "two", "three" }; + var lv = new ListView () { Width = Dim.Fill (), Height = Dim.Fill () }; + lv.RowRender += (s, _) => rendered = true; + Application.Top.Add (lv); + Application.Begin (Application.Top); + Assert.False (rendered); - public bool IsMarked (int item) - { - throw new NotImplementedException (); - } - - public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0) - { - throw new NotImplementedException (); - } - - public void SetMark (int item, bool value) - { - throw new NotImplementedException (); - } - - public IList ToList () - { - return new List () { "One", "Two", "Three" }; + lv.SetSource (source); + lv.Draw (); + Assert.True (rendered); + } + + [Fact] + [AutoInitShutdown] + public void EnsureSelectedItemVisible_Top () + { + var source = new List () { "First", "Second" }; + ListView lv = new ListView (source) { Width = Dim.Fill (), Height = 1 }; + lv.SelectedItem = 1; + Application.Top.Add (lv); + Application.Begin (Application.Top); + + Assert.Equal ("Second ", GetContents (0)); + Assert.Equal (new (' ', 7), GetContents (1)); + + lv.MoveUp (); + lv.Draw (); + + Assert.Equal ("First ", GetContents (0)); + Assert.Equal (new (' ', 7), GetContents (1)); + + string GetContents (int line) + { + var item = ""; + for (int i = 0; i < 7; i++) { + item += Application.Driver.Contents [line, i].Rune; } + return item; } + } - [Fact] - public void KeyBindings_Command () - { - List source = new List () { "One", "Two", "Three" }; - ListView lv = new ListView (source) { Height = 2, AllowsMarking = true }; - lv.BeginInit (); lv.EndInit (); - Assert.Equal (-1, lv.SelectedItem); - Assert.True (lv.NewKeyDownEvent (new (KeyCode.CursorDown))); - Assert.Equal (0, lv.SelectedItem); - Assert.True (lv.NewKeyDownEvent (new (KeyCode.CursorUp))); - Assert.Equal (0, lv.SelectedItem); - Assert.True (lv.NewKeyDownEvent (new (KeyCode.PageDown))); - Assert.Equal (2, lv.SelectedItem); - Assert.Equal (2, lv.TopItem); - Assert.True (lv.NewKeyDownEvent (new (KeyCode.PageUp))); - Assert.Equal (0, lv.SelectedItem); - Assert.Equal (0, lv.TopItem); - Assert.False (lv.Source.IsMarked (lv.SelectedItem)); - Assert.True (lv.NewKeyDownEvent (new (KeyCode.Space))); - Assert.True (lv.Source.IsMarked (lv.SelectedItem)); - var opened = false; - lv.OpenSelectedItem += (s, _) => opened = true; - Assert.True (lv.NewKeyDownEvent (new (KeyCode.Enter))); - Assert.True (opened); - Assert.True (lv.NewKeyDownEvent (new (KeyCode.End))); - Assert.Equal (2, lv.SelectedItem); - Assert.True (lv.NewKeyDownEvent (new (KeyCode.Home))); - Assert.Equal (0, lv.SelectedItem); + [Fact] + [AutoInitShutdown] + public void Ensures_Visibility_SelectedItem_On_MoveDown_And_MoveUp () + { + var source = new List (); + for (int i = 0; i < 20; i++) { + source.Add ($"Line{i}"); } + var lv = new ListView (source) { Width = Dim.Fill (), Height = Dim.Fill () }; + var win = new Window (); + win.Add (lv); + Application.Top.Add (win); + Application.Begin (Application.Top); + ((FakeDriver)Application.Driver).SetBufferSize (12, 12); + Application.Refresh (); - [Fact] - [AutoInitShutdown] - public void RowRender_Event () - { - var rendered = false; - var source = new List () { "one", "two", "three" }; - var lv = new ListView () { Width = Dim.Fill (), Height = Dim.Fill () }; - lv.RowRender += (s, _) => rendered = true; - Application.Top.Add (lv); - Application.Begin (Application.Top); - Assert.False (rendered); - - lv.SetSource (source); - lv.Draw (); - Assert.True (rendered); - } - - [Fact] - [AutoInitShutdown] - public void EnsureSelectedItemVisible_Top () - { - var source = new List () { "First", "Second" }; - ListView lv = new ListView (source) { Width = Dim.Fill (), Height = 1 }; - lv.SelectedItem = 1; - Application.Top.Add (lv); - Application.Begin (Application.Top); - - Assert.Equal ("Second ", GetContents (0)); - Assert.Equal (new (' ', 7), GetContents (1)); - - lv.MoveUp (); - lv.Draw (); - - Assert.Equal ("First ", GetContents (0)); - Assert.Equal (new (' ', 7), GetContents (1)); - - string GetContents (int line) - { - var item = ""; - for (int i = 0; i < 7; i++) { - item += Application.Driver.Contents [line, i].Rune; - } - return item; - } - } - - [Fact] - [AutoInitShutdown] - public void Ensures_Visibility_SelectedItem_On_MoveDown_And_MoveUp () - { - var source = new List (); - for (int i = 0; i < 20; i++) { - source.Add ($"Line{i}"); - } - var lv = new ListView (source) { Width = Dim.Fill (), Height = Dim.Fill () }; - var win = new Window (); - win.Add (lv); - Application.Top.Add (win); - Application.Begin (Application.Top); - ((FakeDriver)Application.Driver).SetBufferSize (12, 12); - Application.Refresh (); - - Assert.Equal (-1, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.Equal (-1, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line0 │ │Line1 │ @@ -275,12 +272,12 @@ namespace Terminal.Gui.ViewsTests { │Line7 │ │Line8 │ │Line9 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.ScrollDown (10)); - lv.Draw (); - Assert.Equal (-1, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.ScrollDown (10)); + lv.Draw (); + Assert.Equal (-1, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line10 │ │Line11 │ @@ -292,12 +289,12 @@ namespace Terminal.Gui.ViewsTests { │Line17 │ │Line18 │ │Line19 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.MoveDown ()); - lv.Draw (); - Assert.Equal (0, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.MoveDown ()); + lv.Draw (); + Assert.Equal (0, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line0 │ │Line1 │ @@ -309,12 +306,12 @@ namespace Terminal.Gui.ViewsTests { │Line7 │ │Line8 │ │Line9 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.MoveEnd ()); - lv.Draw (); - Assert.Equal (19, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.MoveEnd ()); + lv.Draw (); + Assert.Equal (19, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line19 │ │ │ @@ -326,12 +323,12 @@ namespace Terminal.Gui.ViewsTests { │ │ │ │ │ │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.ScrollUp (20)); - lv.Draw (); - Assert.Equal (19, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.ScrollUp (20)); + lv.Draw (); + Assert.Equal (19, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line0 │ │Line1 │ @@ -343,12 +340,12 @@ namespace Terminal.Gui.ViewsTests { │Line7 │ │Line8 │ │Line9 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.MoveDown ()); - lv.Draw (); - Assert.Equal (19, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.MoveDown ()); + lv.Draw (); + Assert.Equal (19, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line10 │ │Line11 │ @@ -360,12 +357,12 @@ namespace Terminal.Gui.ViewsTests { │Line17 │ │Line18 │ │Line19 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.ScrollUp (20)); - lv.Draw (); - Assert.Equal (19, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.ScrollUp (20)); + lv.Draw (); + Assert.Equal (19, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line0 │ │Line1 │ @@ -377,12 +374,12 @@ namespace Terminal.Gui.ViewsTests { │Line7 │ │Line8 │ │Line9 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.MoveDown ()); - lv.Draw (); - Assert.Equal (19, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.MoveDown ()); + lv.Draw (); + Assert.Equal (19, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line10 │ │Line11 │ @@ -394,12 +391,12 @@ namespace Terminal.Gui.ViewsTests { │Line17 │ │Line18 │ │Line19 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.MoveHome ()); - lv.Draw (); - Assert.Equal (0, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.MoveHome ()); + lv.Draw (); + Assert.Equal (0, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line0 │ │Line1 │ @@ -411,12 +408,12 @@ namespace Terminal.Gui.ViewsTests { │Line7 │ │Line8 │ │Line9 │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.ScrollDown (20)); - lv.Draw (); - Assert.Equal (0, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.ScrollDown (20)); + lv.Draw (); + Assert.Equal (0, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line19 │ │ │ @@ -428,12 +425,12 @@ namespace Terminal.Gui.ViewsTests { │ │ │ │ │ │ -└──────────┘", output); +└──────────┘", _output); - Assert.True (lv.MoveUp ()); - lv.Draw (); - Assert.Equal (0, lv.SelectedItem); - TestHelpers.AssertDriverContentsWithFrameAre (@" + Assert.True (lv.MoveUp ()); + lv.Draw (); + Assert.Equal (0, lv.SelectedItem); + TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────┐ │Line0 │ │Line1 │ @@ -445,167 +442,166 @@ namespace Terminal.Gui.ViewsTests { │Line7 │ │Line8 │ │Line9 │ -└──────────┘", output); +└──────────┘", _output); + } + + [Fact] + public void SetSource_Preserves_ListWrapper_Instance_If_Not_Null () + { + var lv = new ListView (new List { "One", "Two" }); + + Assert.NotNull (lv.Source); + + lv.SetSource (null); + Assert.NotNull (lv.Source); + + lv.Source = null; + Assert.Null (lv.Source); + + lv = new ListView (new List { "One", "Two" }); + Assert.NotNull (lv.Source); + + lv.SetSourceAsync (null); + Assert.NotNull (lv.Source); + } + + [Fact] + public void ListWrapper_StartsWith () + { + var lw = new ListWrapper (new List { "One", "Two", "Three" }); + + Assert.Equal (1, lw.StartsWith ("t")); + Assert.Equal (1, lw.StartsWith ("tw")); + Assert.Equal (2, lw.StartsWith ("th")); + Assert.Equal (1, lw.StartsWith ("T")); + Assert.Equal (1, lw.StartsWith ("TW")); + Assert.Equal (2, lw.StartsWith ("TH")); + + lw = new ListWrapper (new List { "One", "Two", "Three" }); + + Assert.Equal (1, lw.StartsWith ("t")); + Assert.Equal (1, lw.StartsWith ("tw")); + Assert.Equal (2, lw.StartsWith ("th")); + Assert.Equal (1, lw.StartsWith ("T")); + Assert.Equal (1, lw.StartsWith ("TW")); + Assert.Equal (2, lw.StartsWith ("TH")); + } + + [Fact, AutoInitShutdown] + public void EnsureSelectedItemVisible_SelectedItem () + { + var source = new List (); + for (int i = 0; i < 10; i++) { + source.Add ($"Item {i}"); } + var lv = new ListView (source) { + Width = 10, + Height = 5 + }; + Application.Top.Add (lv); + Application.Begin (Application.Top); - [Fact] - public void SetSource_Preserves_ListWrapper_Instance_If_Not_Null () - { - var lv = new ListView (new List { "One", "Two" }); - - Assert.NotNull (lv.Source); - - lv.SetSource (null); - Assert.NotNull (lv.Source); - - lv.Source = null; - Assert.Null (lv.Source); - - lv = new ListView (new List { "One", "Two" }); - Assert.NotNull (lv.Source); - - lv.SetSourceAsync (null); - Assert.NotNull (lv.Source); - } - - [Fact] - public void ListWrapper_StartsWith () - { - var lw = new ListWrapper (new List { "One", "Two", "Three" }); - - Assert.Equal (1, lw.StartsWith ("t")); - Assert.Equal (1, lw.StartsWith ("tw")); - Assert.Equal (2, lw.StartsWith ("th")); - Assert.Equal (1, lw.StartsWith ("T")); - Assert.Equal (1, lw.StartsWith ("TW")); - Assert.Equal (2, lw.StartsWith ("TH")); - - lw = new ListWrapper (new List { "One", "Two", "Three" }); - - Assert.Equal (1, lw.StartsWith ("t")); - Assert.Equal (1, lw.StartsWith ("tw")); - Assert.Equal (2, lw.StartsWith ("th")); - Assert.Equal (1, lw.StartsWith ("T")); - Assert.Equal (1, lw.StartsWith ("TW")); - Assert.Equal (2, lw.StartsWith ("TH")); - } - - [Fact, AutoInitShutdown] - public void EnsureSelectedItemVisible_SelectedItem () - { - var source = new List (); - for (int i = 0; i < 10; i++) { - source.Add ($"Item {i}"); - } - var lv = new ListView (source) { - Width = 10, - Height = 5 - }; - Application.Top.Add (lv); - Application.Begin (Application.Top); - - TestHelpers.AssertDriverContentsWithFrameAre (@" + TestHelpers.AssertDriverContentsWithFrameAre (@" Item 0 Item 1 Item 2 Item 3 -Item 4", output); +Item 4", _output); - // EnsureSelectedItemVisible is auto enabled on the OnSelectedChanged - lv.SelectedItem = 6; - Application.Refresh (); - TestHelpers.AssertDriverContentsWithFrameAre (@" + // EnsureSelectedItemVisible is auto enabled on the OnSelectedChanged + lv.SelectedItem = 6; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" Item 2 Item 3 Item 4 Item 5 -Item 6", output); - } - - [Fact] - public void SelectedItem_Get_Set () - { - var lv = new ListView (new List { "One", "Two", "Three" }); - Assert.Equal (-1, lv.SelectedItem); - Assert.Throws (() => lv.SelectedItem = 3); - var exception = Record.Exception (() => lv.SelectedItem = -1); - Assert.Null (exception); - } - - [Fact] - public void OnEnter_Does_Not_Throw_Exception () - { - var lv = new ListView (); - var top = new View (); - top.Add (lv); - var exception = Record.Exception (lv.SetFocus); - Assert.Null (exception); - } - -// No longer needed given PR #2920 -// [Fact, AutoInitShutdown] -// public void Clicking_On_Border_Is_Ignored () -// { -// var selected = ""; -// var lv = new ListView { -// Height = 5, -// Width = 7, -// BorderStyle = LineStyle.Single -// }; -// lv.SetSource (new List { "One", "Two", "Three", "Four" }); -// lv.SelectedItemChanged += (s, e) => selected = e.Value.ToString (); -// Application.Top.Add (lv); -// Application.Begin (Application.Top); - -// Assert.Equal (new Thickness (1), lv.Border.Thickness); -// Assert.Equal (-1, lv.SelectedItem); -// Assert.Equal ("", lv.Text); -// TestHelpers.AssertDriverContentsWithFrameAre (@" -//┌─────┐ -//│One │ -//│Two │ -//│Three│ -//└─────┘", output); - -// Assert.True (lv.MouseEvent (new MouseEvent { -// X = 0, -// Y = 0, -// Flags = MouseFlags.Button1Clicked -// })); -// Assert.Equal ("", selected); -// Assert.Equal (-1, lv.SelectedItem); - -// Assert.True (lv.MouseEvent (new MouseEvent { -// X = 0, -// Y = 1, -// Flags = MouseFlags.Button1Clicked -// })); -// Assert.Equal ("One", selected); -// Assert.Equal (0, lv.SelectedItem); - -// Assert.True (lv.MouseEvent (new MouseEvent { -// X = 0, -// Y = 2, -// Flags = MouseFlags.Button1Clicked -// })); -// Assert.Equal ("Two", selected); -// Assert.Equal (1, lv.SelectedItem); - -// Assert.True (lv.MouseEvent (new MouseEvent { -// X = 0, -// Y = 3, -// Flags = MouseFlags.Button1Clicked -// })); -// Assert.Equal ("Three", selected); -// Assert.Equal (2, lv.SelectedItem); - -// Assert.True (lv.MouseEvent (new MouseEvent { -// X = 0, -// Y = 4, -// Flags = MouseFlags.Button1Clicked -// })); -// Assert.Equal ("Three", selected); -// Assert.Equal (2, lv.SelectedItem); -// } +Item 6", _output); } + + [Fact] + public void SelectedItem_Get_Set () + { + var lv = new ListView (new List { "One", "Two", "Three" }); + Assert.Equal (-1, lv.SelectedItem); + Assert.Throws (() => lv.SelectedItem = 3); + var exception = Record.Exception (() => lv.SelectedItem = -1); + Assert.Null (exception); + } + + [Fact] + public void OnEnter_Does_Not_Throw_Exception () + { + var lv = new ListView (); + var top = new View (); + top.Add (lv); + var exception = Record.Exception (lv.SetFocus); + Assert.Null (exception); + } + + // No longer needed given PR #2920 + // [Fact, AutoInitShutdown] + // public void Clicking_On_Border_Is_Ignored () + // { + // var selected = ""; + // var lv = new ListView { + // Height = 5, + // Width = 7, + // BorderStyle = LineStyle.Single + // }; + // lv.SetSource (new List { "One", "Two", "Three", "Four" }); + // lv.SelectedItemChanged += (s, e) => selected = e.Value.ToString (); + // Application.Top.Add (lv); + // Application.Begin (Application.Top); + + // Assert.Equal (new Thickness (1), lv.Border.Thickness); + // Assert.Equal (-1, lv.SelectedItem); + // Assert.Equal ("", lv.Text); + // TestHelpers.AssertDriverContentsWithFrameAre (@" + //┌─────┐ + //│One │ + //│Two │ + //│Three│ + //└─────┘", output); + + // Assert.True (lv.MouseEvent (new MouseEvent { + // X = 0, + // Y = 0, + // Flags = MouseFlags.Button1Clicked + // })); + // Assert.Equal ("", selected); + // Assert.Equal (-1, lv.SelectedItem); + + // Assert.True (lv.MouseEvent (new MouseEvent { + // X = 0, + // Y = 1, + // Flags = MouseFlags.Button1Clicked + // })); + // Assert.Equal ("One", selected); + // Assert.Equal (0, lv.SelectedItem); + + // Assert.True (lv.MouseEvent (new MouseEvent { + // X = 0, + // Y = 2, + // Flags = MouseFlags.Button1Clicked + // })); + // Assert.Equal ("Two", selected); + // Assert.Equal (1, lv.SelectedItem); + + // Assert.True (lv.MouseEvent (new MouseEvent { + // X = 0, + // Y = 3, + // Flags = MouseFlags.Button1Clicked + // })); + // Assert.Equal ("Three", selected); + // Assert.Equal (2, lv.SelectedItem); + + // Assert.True (lv.MouseEvent (new MouseEvent { + // X = 0, + // Y = 4, + // Flags = MouseFlags.Button1Clicked + // })); + // Assert.Equal ("Three", selected); + // Assert.Equal (2, lv.SelectedItem); + // } }