// // ListView.cs: ListView control // // Authors: // Miguel de Icaza (miguel@gnome.org) // using System; using System.Collections; using System.Collections.Generic; using NStack; namespace Terminal.Gui { public interface IListDataSource { int Count { get; } void Render (int item, int col, int line, int width); } /// /// ListView widget renders a list of data. /// /// /// public class ListView : View { int top; int selected; class ListWrapper : IListDataSource { IList src; public ListView Container; public ConsoleDriver Driver; public ListWrapper (IList source) { this.src = source; } public int Count => src.Count; void RenderUstr (ustring ustr, int col, int line, int width) { int byteLen = ustr.Length; int used = 0; for (int i = 0; i < byteLen;) { (var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen); var count = Rune.ColumnWidth (rune); if (used+count >= width) break; Driver.AddRune (rune); used += count; i += size; } for (; used < width; used++) { Driver.AddRune (' '); } } public void Render (int item, int col, int line, int width) { Container.Move (col, line); var t = src [item]; if (t is ustring) { RenderUstr (t as ustring, col, line, width); } else if (t is string) { RenderUstr (t as string, col, line, width); } else RenderUstr (((string)t).ToString (), col, line, width); } } IListDataSource source; /// /// Gets or sets the IListDataSource backing this view. /// /// The source. public IListDataSource Source { get => source; set { if (value == null) throw new ArgumentNullException ("value"); source = value; top = 0; selected = 0; SetNeedsDisplay (); } } bool allowsMarking; /// /// Gets or sets a value indicating whether this allows items to be marked. /// /// true if allows marking elements of the list; otherwise, false. public bool AllowsMarking { get => allowsMarking; set { allowsMarking = value; SetNeedsDisplay (); } } /// /// Gets or sets the item that is displayed at the top of the listview /// /// The top item. public int TopItem { get => top; set { if (top < 0 || top >= source.Count) throw new ArgumentException ("value"); top = value; SetNeedsDisplay (); } } /// /// Gets or sets the currently selecteded item. /// /// The selected item. public int SelectedItem { get => selected; set { if (selected < 0 || selected >= source.Count) throw new ArgumentException ("value"); selected = value; if (selected < top) top = selected; else if (selected >= top + Frame.Height) top = selected; } } static IListDataSource MakeWrapper (IList source) { return new ListWrapper (source); } public ListView (Rect rect, IList source, (ustring title, int width) [] headers = null) : this (rect, MakeWrapper (source), headers) { ((ListWrapper)(Source)).Container = this; ((ListWrapper)(Source)).Driver = Driver; } public ListView (Rect rect, IListDataSource source, (ustring title, int width) [] headers = null) : base (rect) { if (source == null) throw new ArgumentNullException (nameof (source)); Source = source; } } }