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