diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs
index 66d8377e8..b4ccd5f0d 100644
--- a/Terminal.Gui/Core/View.cs
+++ b/Terminal.Gui/Core/View.cs
@@ -1342,8 +1342,8 @@ namespace Terminal.Gui {
// Draw the subview
// Use the view's bounds (view-relative; Location will always be (0,0)
if (view.Visible && view.Frame.Width > 0 && view.Frame.Height > 0) {
- view.Redraw (view.Bounds);
view.OnDrawContent (view.Bounds);
+ view.Redraw (view.Bounds);
}
}
view.NeedDisplay = Rect.Empty;
diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs
index 41b1ae856..c009146df 100644
--- a/Terminal.Gui/Views/ListView.cs
+++ b/Terminal.Gui/Views/ListView.cs
@@ -34,6 +34,11 @@ namespace Terminal.Gui {
///
int Count { 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.
///
@@ -45,10 +50,11 @@ namespace Terminal.Gui {
/// 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);
+ 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.
@@ -103,7 +109,7 @@ namespace Terminal.Gui {
///
///
public class ListView : View {
- int top;
+ int top, left;
int selected;
IListDataSource source;
@@ -146,7 +152,7 @@ namespace Terminal.Gui {
///
/// An item implementing the IList interface.
///
- /// Use the property to set a new source and use custome rendering.
+ /// Use the property to set a new source and use custom rendering.
///
public Task SetSourceAsync (IList source)
{
@@ -211,6 +217,28 @@ namespace Terminal.Gui {
}
}
+ ///
+ /// Gets or sets the left column where the item start to be displayed at on the .
+ ///
+ /// The left position.
+ public int LeftItem {
+ get => left;
+ set {
+ if (source == null)
+ return;
+
+ if (left < 0 || top >= source.Count)
+ throw new ArgumentException ("value");
+ left = value;
+ SetNeedsDisplay ();
+ }
+ }
+
+ ///
+ /// Gets the widest item.
+ ///
+ public int Maxlength => (source?.Length) ?? 0;
+
///
/// Gets or sets the index of the currently selected item.
///
@@ -229,7 +257,6 @@ namespace Terminal.Gui {
}
}
-
static IListDataSource MakeWrapper (IList source)
{
return new ListWrapper (source);
@@ -301,6 +328,7 @@ namespace Terminal.Gui {
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;
@@ -320,7 +348,7 @@ namespace Terminal.Gui {
Driver.AddRune (source.IsMarked (item) ? (AllowsMultipleSelection ? Driver.Checked : Driver.Selected) : (AllowsMultipleSelection ? Driver.UnChecked : Driver.UnSelected));
Driver.AddRune (' ');
}
- Source.Render (this, Driver, isSelected, item, col, row, f.Width - col);
+ Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start);
}
}
}
@@ -572,6 +600,26 @@ namespace Terminal.Gui {
SetNeedsDisplay ();
}
+ ///
+ /// Scrolls the view right.
+ ///
+ /// Number of columns to scroll right.
+ public virtual void ScrollRight (int cols)
+ {
+ left = Math.Max (Math.Min (left + cols, Maxlength - 1), 0);
+ SetNeedsDisplay ();
+ }
+
+ ///
+ /// Scrolls the view left.
+ ///
+ /// Number of columns to scroll left.
+ public virtual void ScrollLeft (int cols)
+ {
+ left = Math.Max (left - cols, 0);
+ SetNeedsDisplay ();
+ }
+
int lastSelectedItem = -1;
private bool allowsMultipleSelection = true;
@@ -639,7 +687,8 @@ namespace Terminal.Gui {
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.WheeledDown && me.Flags != MouseFlags.WheeledUp &&
+ me.Flags != MouseFlags.WheeledRight && me.Flags != MouseFlags.WheeledLeft)
return false;
if (!HasFocus && CanFocus) {
@@ -656,6 +705,12 @@ namespace Terminal.Gui {
} 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) {
@@ -687,7 +742,7 @@ namespace Terminal.Gui {
public class ListWrapper : IListDataSource {
IList src;
BitArray marks;
- int count;
+ int count, len;
///
/// Initializes a new instance of given an
@@ -698,7 +753,8 @@ namespace Terminal.Gui {
if (source != null) {
count = source.Count;
marks = new BitArray (count);
- this.src = source;
+ src = source;
+ len = GetMaxLengthItem ();
}
}
@@ -707,11 +763,42 @@ namespace Terminal.Gui {
///
public int Count => src != null ? src.Count : 0;
- void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width)
+ ///
+ /// Gets the maximum item length in the .
+ ///
+ public int Length => len;
+
+ int GetMaxLengthItem ()
+ {
+ if (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 ustring u) {
+ l = u.RuneCount;
+ } 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, ustring ustr, int col, int line, int width, int start = 0)
{
int byteLen = ustr.Length;
int used = 0;
- for (int i = 0; i < byteLen;) {
+ for (int i = start; i < byteLen;) {
(var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen);
var count = Rune.ColumnWidth (rune);
if (used + count > width)
@@ -735,19 +822,21 @@ namespace Terminal.Gui {
/// The col where to move.
/// The line where to move.
/// The item width.
- public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width)
+ /// The index of the string to be displayed.
+ 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, ustring.Make (""), col, line, width);
} else {
- if (t is ustring) {
- RenderUstr (driver, (ustring)t, col, line, width);
- } else if (t is string) {
- RenderUstr (driver, (string)t, col, line, width);
- } else
- RenderUstr (driver, t.ToString (), col, line, width);
+ if (t is ustring 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);
+ }
}
}
@@ -793,14 +882,14 @@ namespace Terminal.Gui {
///
public int Item { get; }
///
- /// The the item.
+ /// The item.
///
public object Value { get; }
///
/// Initializes a new instance of
///
- /// The index of the the item.
+ /// The index of the item.
/// The item
public ListViewItemEventArgs (int item, object value)
{
diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs
index 6163fbe9c..7cffd2a63 100644
--- a/Terminal.Gui/Views/ScrollBarView.cs
+++ b/Terminal.Gui/Views/ScrollBarView.cs
@@ -139,6 +139,8 @@ namespace Terminal.Gui {
} else {
position = Math.Max (position + max, 0);
}
+ } else if (max < 0) {
+ position = Math.Max (position + max, 0);
}
OnChangedPosition ();
SetNeedsDisplay ();
@@ -173,6 +175,7 @@ namespace Terminal.Gui {
Visible = true;
} else {
Visible = false;
+ Position = 0;
}
Width = vertical ? 1 : Dim.Width (Host);
Height = vertical ? Dim.Height (Host) : 1;
diff --git a/UICatalog/Scenarios/ListViewWithSelection.cs b/UICatalog/Scenarios/ListViewWithSelection.cs
index dac0f619e..b729ee0c3 100644
--- a/UICatalog/Scenarios/ListViewWithSelection.cs
+++ b/UICatalog/Scenarios/ListViewWithSelection.cs
@@ -25,7 +25,7 @@ namespace UICatalog {
Height = 1,
};
Win.Add (_customRenderCB);
- _customRenderCB.Toggled += _customRenderCB_Toggled; ;
+ _customRenderCB.Toggled += _customRenderCB_Toggled;
_allowMarkingCB = new CheckBox ("Allow Marking") {
X = Pos.Right (_customRenderCB) + 1,
@@ -56,6 +56,9 @@ namespace UICatalog {
Win.Add (_listView);
var vertical = new ScrollBarView (_listView, true);
+ var horizontal = new ScrollBarView (_listView, false);
+ vertical.OtherScrollBarView = horizontal;
+ horizontal.OtherScrollBarView = vertical;
vertical.ChangedPosition += () => {
_listView.TopItem = vertical.Position;
@@ -65,13 +68,26 @@ namespace UICatalog {
_listView.SetNeedsDisplay ();
};
+ horizontal.ChangedPosition += () => {
+ _listView.LeftItem = horizontal.Position;
+ if (_listView.LeftItem != horizontal.Position) {
+ horizontal.Position = _listView.LeftItem;
+ }
+ _listView.SetNeedsDisplay ();
+ };
+
_listView.DrawContent += (e) => {
- vertical.Size = _listView.Source.Count;
+ vertical.Size = _listView.Source.Count - 1;
vertical.Position = _listView.TopItem;
- vertical.ColorScheme = _listView.ColorScheme;
+ horizontal.Size = _listView.Maxlength;
+ horizontal.Position = _listView.LeftItem;
+ vertical.ColorScheme = horizontal.ColorScheme = _listView.ColorScheme;
if (vertical.ShowScrollIndicator) {
vertical.Redraw (e);
}
+ if (horizontal.ShowScrollIndicator) {
+ horizontal.Redraw (e);
+ }
};
_listView.SetSource (_scenarios);
@@ -114,15 +130,16 @@ namespace UICatalog {
int _nameColumnWidth = 30;
private List scenarios;
BitArray marks;
- int count;
+ int count, len;
public List Scenarios {
- get => scenarios;
+ get => scenarios;
set {
if (value != null) {
count = value.Count;
marks = new BitArray (count);
scenarios = value;
+ len = GetMaxLengthItem ();
}
}
}
@@ -135,14 +152,16 @@ namespace UICatalog {
public int Count => Scenarios != null ? Scenarios.Count : 0;
+ public int Length => len;
+
public ScenarioListDataSource (List itemList) => Scenarios = itemList;
- public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width)
+ public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0)
{
container.Move (col, line);
// Equivalent to an interpolated string like $"{Scenarios[item].Name, -widtestname}"; if such a thing were possible
var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [item]));
- RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width);
+ RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width, start);
}
public void SetMark (int item, bool value)
@@ -151,11 +170,30 @@ namespace UICatalog {
marks [item] = value;
}
+ int GetMaxLengthItem ()
+ {
+ if (scenarios?.Count == 0) {
+ return 0;
+ }
+
+ int maxLength = 0;
+ for (int i = 0; i < scenarios.Count; i++) {
+ var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [i]));
+ var sc = $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [i])}";
+ var l = sc.Length;
+ if (l > maxLength) {
+ maxLength = l;
+ }
+ }
+
+ return maxLength;
+ }
+
// A slightly adapted method from: https://github.com/migueldeicaza/gui.cs/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461
- private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width)
+ private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width, int start = 0)
{
int used = 0;
- int index = 0;
+ int index = start;
while (index < ustr.Length) {
(var rune, var size) = Utf8.DecodeRune (ustr, index, index - ustr.Length);
var count = Rune.ColumnWidth (rune);
diff --git a/UICatalog/Scenarios/ListsAndCombos.cs b/UICatalog/Scenarios/ListsAndCombos.cs
index 910d3496d..dda51619b 100644
--- a/UICatalog/Scenarios/ListsAndCombos.cs
+++ b/UICatalog/Scenarios/ListsAndCombos.cs
@@ -39,6 +39,41 @@ namespace UICatalog.Scenarios {
listview.SelectedItemChanged += (ListViewItemEventArgs e) => lbListView.Text = items [listview.SelectedItem];
Win.Add (lbListView, listview);
+ var vertical = new ScrollBarView (listview, true);
+ var horizontal = new ScrollBarView (listview, false);
+ vertical.OtherScrollBarView = horizontal;
+ horizontal.OtherScrollBarView = vertical;
+
+ vertical.ChangedPosition += () => {
+ listview.TopItem = vertical.Position;
+ if (listview.TopItem != vertical.Position) {
+ vertical.Position = listview.TopItem;
+ }
+ listview.SetNeedsDisplay ();
+ };
+
+ horizontal.ChangedPosition += () => {
+ listview.LeftItem = horizontal.Position;
+ if (listview.LeftItem != horizontal.Position) {
+ horizontal.Position = listview.LeftItem;
+ }
+ listview.SetNeedsDisplay ();
+ };
+
+ listview.DrawContent += (e) => {
+ vertical.Size = listview.Source.Count - 1;
+ vertical.Position = listview.TopItem;
+ horizontal.Size = listview.Maxlength;
+ horizontal.Position = listview.LeftItem;
+ vertical.ColorScheme = horizontal.ColorScheme = listview.ColorScheme;
+ if (vertical.ShowScrollIndicator) {
+ vertical.Redraw (e);
+ }
+ if (horizontal.ShowScrollIndicator) {
+ horizontal.Redraw (e);
+ }
+ };
+
// ComboBox
var lbComboBox = new Label ("ComboBox") {
ColorScheme = Colors.TopLevel,
diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs
index d71088b65..12b37bf18 100644
--- a/UICatalog/UICatalog.cs
+++ b/UICatalog/UICatalog.cs
@@ -498,31 +498,58 @@ namespace UICatalog {
}
internal class ScenarioListDataSource : IListDataSource {
+ private readonly int len;
+
public List Scenarios { get; set; }
public bool IsMarked (int item) => false;
public int Count => Scenarios.Count;
- public ScenarioListDataSource (List itemList) => Scenarios = itemList;
+ public int Length => len;
- public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width)
+ public ScenarioListDataSource (List itemList)
+ {
+ Scenarios = itemList;
+ len = GetMaxLengthItem ();
+ }
+
+ public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0)
{
container.Move (col, line);
// Equivalent to an interpolated string like $"{Scenarios[item].Name, -widtestname}"; if such a thing were possible
var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [item]));
- RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width);
+ RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width, start);
}
public void SetMark (int item, bool value)
{
}
+ int GetMaxLengthItem ()
+ {
+ if (Scenarios?.Count == 0) {
+ return 0;
+ }
+
+ int maxLength = 0;
+ for (int i = 0; i < Scenarios.Count; i++) {
+ var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [i]));
+ var sc = $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [i])}";
+ var l = sc.Length;
+ if (l > maxLength) {
+ maxLength = l;
+ }
+ }
+
+ return maxLength;
+ }
+
// A slightly adapted method from: https://github.com/migueldeicaza/gui.cs/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461
- private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width)
+ private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width, int start = 0)
{
int used = 0;
- int index = 0;
+ int index = start;
while (index < ustr.Length) {
(var rune, var size) = Utf8.DecodeRune (ustr, index, index - ustr.Length);
var count = Rune.ColumnWidth (rune);