//
// ComboBox.cs: ComboBox control
//
// Authors:
// Ross Ferguson (ross.c.ferguson@btinternet.com)
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Terminal.Gui;
///
/// Provides a drop-down list of items the user can select from.
///
public class ComboBox : View {
class ComboListView : ListView {
int _highlighted = -1;
bool _isFocusing;
ComboBox _container;
bool _hideDropdownListOnClick;
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)
{
_container = container ?? throw new ArgumentNullException (nameof (container), "ComboBox container cannot be null.");
HideDropdownListOnClick = hideDropdownListOnClick;
AddCommand (Command.LineUp, () => _container.MoveUpList ());
}
public bool HideDropdownListOnClick {
get => _hideDropdownListOnClick;
set => _hideDropdownListOnClick = WantContinuousButtonPressed = value;
}
public override bool MouseEvent (MouseEvent me)
{
bool res = false;
bool 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;
}
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 OnDrawContent (Rect contentArea)
{
var current = ColorScheme.Focus;
Driver.SetAttribute (current);
Move (0, 0);
var f = Frame;
int 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 ((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 ? Glyphs.Checked : Glyphs.Selected : AllowsMultipleSelection ? Glyphs.UnChecked : Glyphs.UnSelected);
Driver.AddRune ((Rune)' ');
}
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 ()
{
bool 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