namespace Terminal.Gui;
///
/// for a which adds a checkbox column as an additional column
/// in the table.
///
///
/// This class wraps another and dynamically serves its rows/cols plus an extra column.
/// Data in the wrapped source can be dynamic (change over time).
///
public abstract class CheckBoxTableSourceWrapperBase : ITableSource
{
private readonly TableView tableView;
///
/// Creates a new instance of the class presenting the data in plus an additional
/// checkbox column.
///
///
/// The this source will be used with. This is required for event
/// registration.
///
/// The original data source of the that you want to add checkboxes to.
public CheckBoxTableSourceWrapperBase (TableView tableView, ITableSource toWrap)
{
Wrapping = toWrap;
this.tableView = tableView;
tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
tableView.MouseClick += TableView_MouseClick;
tableView.CellToggled += TableView_CellToggled;
}
///
/// Gets or sets the character to use for checked entries. Defaults to
///
public Rune CheckedRune { get; set; } = Glyphs.CheckStateChecked;
///
/// Gets or sets the character to use for checked entry when is true. Defaults to
///
///
public Rune RadioCheckedRune { get; set; } = Glyphs.Selected;
///
/// Gets or sets the character to use for unchecked entries when is true. Defaults
/// to
///
public Rune RadioUnCheckedRune { get; set; } = Glyphs.UnSelected;
///
/// Gets or sets the character to use for UnChecked entries. Defaults to
///
public Rune UnCheckedRune { get; set; } = Glyphs.CheckStateUnChecked;
/// Gets or sets whether to only allow a single row to be toggled at once (Radio button).
public bool UseRadioButtons { get; set; }
/// Gets the that this instance is wrapping.
public ITableSource Wrapping { get; }
///
public object this [int row, int col]
{
get
{
if (col == 0)
{
if (UseRadioButtons)
{
return IsChecked (row) ? RadioCheckedRune : RadioUnCheckedRune;
}
return IsChecked (row) ? CheckedRune : UnCheckedRune;
}
return Wrapping [row, col - 1];
}
}
///
public int Rows => Wrapping.Rows;
///
public int Columns => Wrapping.Columns + 1;
///
public string [] ColumnNames
{
get
{
List toReturn = Wrapping.ColumnNames.ToList ();
toReturn.Insert (0, " ");
return toReturn.ToArray ();
}
}
/// Clears the toggled state of all rows.
protected abstract void ClearAllToggles ();
/// Returns true if is checked.
///
///
protected abstract bool IsChecked (int row);
///
/// Called when the 'toggled all' action is performed. This should change state from 'some selected' to 'all
/// selected' or clear selection if all area already selected.
///
protected abstract void ToggleAllRows ();
/// Flips the checked state of the given /
///
protected abstract void ToggleRow (int row);
///
/// Flips the checked state for a collection of rows. If some (but not all) are selected they should flip to all
/// selected.
///
///
protected abstract void ToggleRows (int [] range);
private void TableView_CellToggled (object sender, CellToggledEventArgs e)
{
// Suppress default toggle behavior when using checkboxes
// and instead handle ourselves
int [] range = tableView.GetAllSelectedCells ().Select (c => c.Y).Distinct ().ToArray ();
if (UseRadioButtons)
{
// multi selection makes it unclear what to toggle in this situation
if (range.Length != 1)
{
e.Cancel = true;
return;
}
ClearAllToggles ();
ToggleRow (range.Single ());
}
else
{
ToggleRows (range);
}
e.Cancel = true;
tableView.SetNeedsDraw ();
}
private void TableView_MouseClick (object sender, MouseEventArgs e)
{
// we only care about clicks (not movements)
if (!e.Flags.HasFlag (MouseFlags.Button1Clicked))
{
return;
}
Point? hit = tableView.ScreenToCell (e.Position.X, e.Position.Y, out int? headerIfAny);
if (headerIfAny.HasValue && headerIfAny.Value == 0)
{
// clicking in header with radio buttons does nothing
if (UseRadioButtons)
{
return;
}
// otherwise it ticks all rows
ToggleAllRows ();
e.Handled = true;
tableView.SetNeedsDraw ();
}
else if (hit.HasValue && hit.Value.X == 0)
{
if (UseRadioButtons)
{
ClearAllToggles ();
ToggleRow (hit.Value.Y);
}
else
{
ToggleRow (hit.Value.Y);
}
e.Handled = true;
tableView.SetNeedsDraw ();
}
}
}