//
// ComboBox.cs: ComboBox control
//
// Authors:
// Ross Ferguson (ross.c.ferguson@btinternet.com)
//
using System;
using System.Collections;
using System.Collections.Generic;
using NStack;
namespace Terminal.Gui {
///
/// Provides a drop-down list of items the user can select from.
///
public class ComboBox : View {
private class ComboListView : ListView {
private int highlighted = -1;
private bool isFocusing;
private ComboBox container;
private bool hideDropdownListOnClick;
public ComboListView (ComboBox container, bool hideDropdownListOnClick)
{
Initialize (container, hideDropdownListOnClick);
}
public ComboListView (ComboBox container, Rect rect, IList source, bool hideDropdownListOnClick) : base (rect, source)
{
Initialize (container, hideDropdownListOnClick);
}
public ComboListView (ComboBox container, IList source, bool hideDropdownListOnClick) : base (source)
{
Initialize (container, hideDropdownListOnClick);
}
private void Initialize (ComboBox container, bool hideDropdownListOnClick)
{
if (container == null)
throw new ArgumentNullException ("ComboBox container cannot be null.", nameof (container));
this.container = container;
HideDropdownListOnClick = hideDropdownListOnClick;
}
public bool HideDropdownListOnClick {
get => hideDropdownListOnClick;
set => hideDropdownListOnClick = WantContinuousButtonPressed = value;
}
public override bool MouseEvent (MouseEvent me)
{
var res = false;
var isMousePositionValid = IsMousePositionValid (me);
if (isMousePositionValid) {
res = base.MouseEvent (me);
}
if (HideDropdownListOnClick && me.Flags == MouseFlags.Button1Clicked) {
if (!isMousePositionValid && !isFocusing) {
container.isShow = false;
container.HideList ();
} else if (isMousePositionValid) {
OnOpenSelectedItem ();
} else {
isFocusing = false;
}
return true;
} else if (me.Flags == MouseFlags.ReportMousePosition && HideDropdownListOnClick) {
if (isMousePositionValid) {
highlighted = Math.Min (TopItem + me.Y, Source.Count);
SetNeedsDisplay ();
}
isFocusing = false;
return true;
}
return res;
}
private bool IsMousePositionValid (MouseEvent me)
{
if (me.X >= 0 && me.X < Frame.Width && me.Y >= 0 && me.Y < Frame.Height) {
return true;
}
return false;
}
public override void Redraw (Rect bounds)
{
var current = ColorScheme.Focus;
Driver.SetAttribute (current);
Move (0, 0);
var f = Frame;
var item = TopItem;
bool focused = HasFocus;
int col = AllowsMarking ? 2 : 0;
int start = LeftItem;
for (int row = 0; row < f.Height; row++, item++) {
bool isSelected = item == container.SelectedItem;
bool isHighlighted = hideDropdownListOnClick && item == highlighted;
Attribute newcolor;
if (isHighlighted || (isSelected && !hideDropdownListOnClick)) {
newcolor = focused ? ColorScheme.Focus : ColorScheme.HotNormal;
} else if (isSelected && hideDropdownListOnClick) {
newcolor = focused ? ColorScheme.HotFocus : ColorScheme.HotNormal;
} else {
newcolor = focused ? GetNormalColor () : 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 (' ');
} 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 ? Driver.Checked : Driver.Selected) : (AllowsMultipleSelection ? Driver.UnChecked : Driver.UnSelected));
Driver.AddRune (' ');
}
Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start);
}
}
}
public override bool OnEnter (View view)
{
if (hideDropdownListOnClick) {
isFocusing = true;
highlighted = container.SelectedItem;
Application.GrabMouse (this);
}
return base.OnEnter (view);
}
public override bool OnLeave (View view)
{
if (hideDropdownListOnClick) {
isFocusing = false;
highlighted = container.SelectedItem;
Application.UngrabMouse ();
}
return base.OnLeave (view);
}
public override bool OnSelectedChanged ()
{
var res = base.OnSelectedChanged ();
highlighted = SelectedItem;
return res;
}
}
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;
// Only need to refresh list if its been added to a container view
if (SuperView != null && SuperView.Subviews.Contains (this)) {
SelectedItem = -1;
search.Text = "";
Search_Changed (this, new TextChangedEventArgs (""));
SetNeedsDisplay ();
}
}
}
///
/// 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)
{
if (source == null) {
Source = null;
} else {
listview.SetSource (source);
Source = listview.Source;
}
}
///
/// This event is raised when the selected item in the has changed.
///
public event EventHandler SelectedItemChanged;
///
/// This event is raised when the drop-down list is expanded.
///
public event EventHandler Expanded;
///
/// This event is raised when the drop-down list is collapsed.
///
public event EventHandler Collapsed;
///
/// This event is raised when the user Double Clicks on an item or presses ENTER to open the selected item.
///
public event EventHandler OpenSelectedItem;
readonly IList searchset = new List