Files
Terminal.Gui/UICatalog/Scenarios/DynamicStatusBar.cs
Tig dcb3b359ad Fixes #2926 - Refactor KeyEvent and KeyEventEventArgs to simplify (#2927)
* Adds basic MainLoop unit tests

* Remove WinChange action from Curses

* Remove WinChange action from Curses

* Remove ProcessInput action from Windows MainLoop

* Simplified MainLoop/ConsoleDriver by making MainLoop internal and moving impt fns to Application

* Modernized Terminal resize events

* Modernized Terminal resize events

* Removed un used property

* for _isWindowsTerminal devenv->wininit; not sure what changed

* Modernized mouse/keyboard events (Action->EventHandler)

* Updated OnMouseEvent API docs

* Using WT_SESSION to detect WT

* removes hacky GetParentProcess

* Updates to fix #2634 (clear last line)

* removes hacky GetParentProcess2

* Addressed mac resize issue

* Addressed mac resize issue

* Removes ConsoleDriver.PrepareToRun, has Init return MainLoop

* Removes unneeded Attribute methods

* Removed GetProcesssName

* Removed GetProcesssName

* Refactored KeyEvent and KeyEventEventArgs into a single class

* Revert "Refactored KeyEvent and KeyEventEventArgs into a single class"

This reverts commit 88a00658db.

* Fixed key repeat issue; reverted stupidity on 1049/1047 confusion

* Updated CSI API Docs

* merge

* Rearranged Event.cs to Keyboard.cs and Mouse.cs

* Renamed KeyEventEventArgs KeyEventArgs

* temp renamed KeyEvent OldKeyEvent

* Merged KeyEvent into KeyEventArgs

* Renamed Application.ProcessKey members

* Renamed Application.ProcessKey members

* Renamed Application.ProcessKey members

* Added Responder.KeyPressed

* Removed unused references

* Fixed arg naming

* InvokeKeybindings->InvokeKeyBindings

* InvokeKeybindings->InvokeKeyBindings

* Fixed unit tests fail

* More progress on refactoring key input; still broken and probably wrong

* Moved OnKeyPressed out of Responder and made ProcessKeyPrssed non-virtual

* Updated API docs

* Moved key handling from Responder to View

* Updated API docs

* Updated HotKey API docs

* Updated shortcut API docs

* Fixed responder unit tests

* Removed Shortcut from View as it is not used

* Removed unneeded OnHotKey override from Button

* Fixed BackTab logic

* Button now uses Key Bindings exclusively

* Button now uses Key Bindings exclusively

* Updated keyboard.md docs

* Fixed unit tests to account for Toplevel handling default button

* Added View.InvokeCommand API

* Modernized RadioGroup

* Removed ColdKey

* Modernized (partially) StatusBar

* Worked around FileDialog issue with Ctrl-F

* Fixed driver unit test; view must be focused to reciev key pressed

* Application code cleanup

* Start on updaing menu

* Menu now mostly works

* Menu Select refinement

* Fixed known menu bugs!

* Enabled HotKey to cause focus- experimental

* Fixes #3022 & adds unit test to prove it

* Actually Fixes #3022 & adds unit test to prove it

* Working through hotkey issues

* Misc fixes

* removed hot/cold key stuff from Keys scenario

* Fixed scenarios

* Simplified shortcut string handling

* Modernized Checkbox

* Modernized TileView

* Updated API docs

* Updated API docs

* attempting to publish v2 docs

* Revert "attempting to publish v2 docs"

This reverts commit 59dcec111b.

* Playing with api docs

* Removed Key.BackTab

* Removed Caps/Scroll/Numlock

* Partial removal of keymodifiers - unit tests pass

* Partial removal of keymodifiers - broke netdriver somewhere

* WindowsDriver & added KeyEventArgsTests

* Fixing menu shortcut/hotkeys - broke Menu.cs into separate files

* Fixed MenuBar!

* Finished modernizing Menu/MenuBar

* Removed Key.a-z. Broke lots of stuff

* checkout@v4

* progress on key mapping and formatting

* VK tests are still failing

* Fixed some unit tests

* Added Hotkey and Keybinding unit tests

* fixed unit test

* All unit tests pass again...

* Fixed broken unit tests

* KeyEventArgs.KeyValue -> AsRune

* Fixed bugs. Still some broken

* Added KeyEventArgs.IsAlpha. Added KeyEventArgs.cast ops. Fixed bugs. Unit tests pass

* Fixed WindowsDriver

* Oops.

* Refactoring based on bdisp's help. Not complete!

* removed calling into subviews from OnKeyBindings

* removed calling into subviews from OnKeyBindings

* Improved View KeyEvent unit tests

* More hotkey unit tests

* BIg change - Got rid of KeyPress w/in Application/Drivers

* Unit tests now pass again

* Refreshed API docs

* Better HotKey logic. More progress. Getting close.

* Fixed handling of shifted chars like ö

* Minor code cleanup

* Minor code cleanup2

* Why is build Action failing?

* Why is build Action failing??

* upgraded to .net8 to try to fix weird CI/CD build errors

* upgraded to .net8 to try to fix weird CI/CD build errors2

* Disabling TextViewTests to diagnose build errors

* reenable TextViewTests to diagnose build errors

* Arrrrrrg

* Merged v2_develop

* Fixed uppercase accented keys in WindowsDriver

* Fixed key binding api docs

* Experimental impl of CommandScope.SubViews for MenuBar

* Removed dead code from application.cs

* Removed dead code from application.cs

* Removed dead code from ConsoleDriver.cs

* Cleaned up some key binding stuff

* Disabled Alt to activate menu for now

* Updated label commands

* Fixed menu bugs. Upgraded menu unit tests

* Fixed unit tests

* Working on NetDriver

* fixed netdriver

* Fixed issues called out by @bdisp CR

* fixed CursesDriver

* added todo to netdriver

* Cherry picked treeview test fix 1b415e5

* Fix NetDriver.

* CommandScope->KeyBindingScope

* Address some tznind feedback

* Refactored KeyBindings big time!

* Added key consts to KeyEventArgs and renamed Key to ConsoleDriverKey

* Fixed some API docs

* Moved ConsoleDriverKey to ConsoleDriver.cs

* Renamed Key->ConsoleDriverKey

* Renamed Key->ConsoleDriverKey

* Renamed Key->ConsoleDriverKey

* renamed file I forgot to rename before

* Updated name and API docs of KeyEventArgs.isAlpha

* Fixed issues with OnKeyUp not doing the right thing.

* Fixed MainLoop.Running never being used

* Fixed MainLoop.Running never being used - unit tests

* Claned up BUGBUG comments

* Disabled a unit test to see why ci/cd tests are failing

* Removed defunct commented code

* Removed more defunct commented code

* Re-eanbled unit test; jsut removing one test case...

* Disabled more...

* Renambed Global->Applicaton and updated scope API docs

* Disabled more unit tests...

* Removed dead code

* Disabled more unit tests...2

* Disabled more unit tests...3

* Renambed Global->Applicaton and updated scope API docs 2

* Added more KeyBinding scope tests

* Added more KeyBinding scope tests2

* ConsoleDriverKey too long. Key too ambiguous. Settled on KeyCode. (Partialy because eventually I want to intro a class named Key).

* KeyEventArgs improvements. cast to Rune must be explicit as it's lossy

* Fixed warnings

* Renamed KeyEventArgs to Key... progress on fixing broken stuff that resulted

* Fix ConsoleKeyMapping bugs.

* Fix NetDriver issue from converting a lower case to a upper case.

* Started migration to Key from KeyCode - e.g. made HotKeys all consistent.

* Fixed build warnings

* Added key defns to Key

* KeyBindings now uses Key vs. KeyCode

* Verified by tweaking UICatalog

* Fixed treeview test ... again

* Renamed ProcessKeyDown/Up to NewKeyDown/Up and OnKeyPressed to OnProcessKeyDown to make things more clear

* Added test AllViews_KeyDown_All_EventsFire unit tests and fixed a few Views that were wrong

* fixed stupid KeyUp event bug

* If key not handled, return false for datefield

* dotnet test --no-restore --verbosity diag

* dotnet test --blame

* run tests on windows

* Fix TestVKPacket unit test and move it to ConsoleKeyMappingTests.cs file.

* Remove unnecessary commented code.

* Tweaked unit tests and removed Key.BareKey

* Fixed little details and updated api docs

* updated api docs

* AddKeyBindingsForHotKey: KeyCode->Key

* Cleaned up more old KeyCode usages. Added TODOs

---------

Co-authored-by: BDisp <bd.bdisp@gmail.com>
2023-12-16 12:04:23 -07:00

656 lines
18 KiB
C#

using System.Text;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using Terminal.Gui;
namespace UICatalog.Scenarios;
[ScenarioMetadata (Name: "Dynamic StatusBar", Description: "Demonstrates how to add and remove a StatusBar and change items dynamically.")]
[ScenarioCategory ("Top Level Windows")]
public class DynamicStatusBar : Scenario {
public override void Init ()
{
Application.Init ();
Application.Top.Add (new DynamicStatusBarSample () { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" });
}
public class DynamicStatusItemList {
public string Title { get; set; }
public StatusItem StatusItem { get; set; }
public DynamicStatusItemList () { }
public DynamicStatusItemList (string title, StatusItem statusItem)
{
Title = title;
StatusItem = statusItem;
}
public override string ToString () => $"{Title}, {StatusItem}";
}
public class DynamicStatusItem {
public string title = "New";
public string action = "";
public string shortcut;
public DynamicStatusItem () { }
public DynamicStatusItem (string title)
{
this.title = title;
}
public DynamicStatusItem (string title, string action, string shortcut = null)
{
this.title = title;
this.action = action;
this.shortcut = shortcut;
}
}
public class DynamicStatusBarSample : Window {
StatusBar _statusBar;
StatusItem _currentStatusItem;
int _currentSelectedStatusBar = -1;
StatusItem _currentEditStatusItem;
ListView _lstItems;
public DynamicStatusItemModel DataContext { get; set; }
public DynamicStatusBarSample () : base ()
{
DataContext = new DynamicStatusItemModel ();
var _frmDelimiter = new FrameView ("Shortcut Delimiter:") {
X = Pos.Center (),
Y = 0,
Width = 25,
Height = 4
};
var _txtDelimiter = new TextField ($"{StatusBar.ShortcutDelimiter}") {
X = Pos.Center (),
Width = 2,
};
_txtDelimiter.TextChanged += (s, _) => StatusBar.ShortcutDelimiter = _txtDelimiter.Text.ToRunes () [0];
_frmDelimiter.Add (_txtDelimiter);
Add (_frmDelimiter);
var _frmStatusBar = new FrameView ("Items:") {
Y = 5,
Width = Dim.Percent (50),
Height = Dim.Fill (2)
};
var _btnAddStatusBar = new Button ("Add a StatusBar") {
Y = 1,
};
_frmStatusBar.Add (_btnAddStatusBar);
var _btnRemoveStatusBar = new Button ("Remove a StatusBar") {
Y = 1
};
_btnRemoveStatusBar.X = Pos.AnchorEnd () - (Pos.Right (_btnRemoveStatusBar) - Pos.Left (_btnRemoveStatusBar));
_frmStatusBar.Add (_btnRemoveStatusBar);
var _btnAdd = new Button (" Add ") {
Y = Pos.Top (_btnRemoveStatusBar) + 2,
};
_btnAdd.X = Pos.AnchorEnd () - (Pos.Right (_btnAdd) - Pos.Left (_btnAdd));
_frmStatusBar.Add (_btnAdd);
_lstItems = new ListView (new List<DynamicStatusItemList> ()) {
ColorScheme = Colors.Dialog,
Y = Pos.Top (_btnAddStatusBar) + 2,
Width = Dim.Fill () - Dim.Width (_btnAdd) - 1,
Height = Dim.Fill (),
};
_frmStatusBar.Add (_lstItems);
var _btnRemove = new Button ("Remove") {
X = Pos.Left (_btnAdd),
Y = Pos.Top (_btnAdd) + 1
};
_frmStatusBar.Add (_btnRemove);
var _btnUp = new Button ("^") {
X = Pos.Right (_lstItems) + 2,
Y = Pos.Top (_btnRemove) + 2
};
_frmStatusBar.Add (_btnUp);
var _btnDown = new Button ("v") {
X = Pos.Right (_lstItems) + 2,
Y = Pos.Top (_btnUp) + 1
};
_frmStatusBar.Add (_btnDown);
Add (_frmStatusBar);
var _frmStatusBarDetails = new DynamicStatusBarDetails ("StatusBar Item Details:") {
X = Pos.Right (_frmStatusBar),
Y = Pos.Top (_frmStatusBar),
Width = Dim.Fill (),
Height = Dim.Fill (4)
};
Add (_frmStatusBarDetails);
_btnUp.Clicked += (s, e) => {
var i = _lstItems.SelectedItem;
var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].StatusItem : null;
if (statusItem != null) {
var items = _statusBar.Items;
if (i > 0) {
items [i] = items [i - 1];
items [i - 1] = statusItem;
DataContext.Items [i] = DataContext.Items [i - 1];
DataContext.Items [i - 1] = new DynamicStatusItemList (statusItem.Title, statusItem);
_lstItems.SelectedItem = i - 1;
_statusBar.SetNeedsDisplay ();
}
}
};
_btnDown.Clicked += (s, e) => {
var i = _lstItems.SelectedItem;
var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].StatusItem : null;
if (statusItem != null) {
var items = _statusBar.Items;
if (i < items.Length - 1) {
items [i] = items [i + 1];
items [i + 1] = statusItem;
DataContext.Items [i] = DataContext.Items [i + 1];
DataContext.Items [i + 1] = new DynamicStatusItemList (statusItem.Title, statusItem);
_lstItems.SelectedItem = i + 1;
_statusBar.SetNeedsDisplay ();
}
}
};
var _btnOk = new Button ("Ok") {
X = Pos.Right (_frmStatusBar) + 20,
Y = Pos.Bottom (_frmStatusBarDetails),
};
Add (_btnOk);
var _btnCancel = new Button ("Cancel") {
X = Pos.Right (_btnOk) + 3,
Y = Pos.Top (_btnOk),
};
_btnCancel.Clicked += (s, e) => {
SetFrameDetails (_currentEditStatusItem);
};
Add (_btnCancel);
_lstItems.SelectedItemChanged += (s, e) => {
SetFrameDetails ();
};
_btnOk.Clicked += (s, e) => {
if (string.IsNullOrEmpty (_frmStatusBarDetails._txtTitle.Text) && _currentEditStatusItem != null) {
MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
} else if (_currentEditStatusItem != null) {
_frmStatusBarDetails._txtTitle.Text = SetTitleText (
_frmStatusBarDetails._txtTitle.Text, _frmStatusBarDetails._txtShortcut.Text);
var statusItem = new DynamicStatusItem (_frmStatusBarDetails._txtTitle.Text,
_frmStatusBarDetails._txtAction.Text,
_frmStatusBarDetails._txtShortcut.Text);
UpdateStatusItem (_currentEditStatusItem, statusItem, _lstItems.SelectedItem);
}
};
_btnAdd.Clicked += (s, e) => {
if (StatusBar == null) {
MessageBox.ErrorQuery ("StatusBar Bar Error", "Must add a StatusBar first!", "Ok");
_btnAddStatusBar.SetFocus ();
return;
}
var frameDetails = new DynamicStatusBarDetails ();
var item = frameDetails.EnterStatusItem ();
if (item == null) {
return;
}
StatusItem newStatusItem = CreateNewStatusBar (item);
_currentSelectedStatusBar++;
_statusBar.AddItemAt (_currentSelectedStatusBar, newStatusItem);
DataContext.Items.Add (new DynamicStatusItemList (newStatusItem.Title, newStatusItem));
_lstItems.MoveDown ();
SetFrameDetails ();
};
_btnRemove.Clicked += (s, e) => {
var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [_lstItems.SelectedItem].StatusItem : null;
if (statusItem != null) {
_statusBar.RemoveItem (_currentSelectedStatusBar);
DataContext.Items.RemoveAt (_lstItems.SelectedItem);
if (_lstItems.Source.Count > 0 && _lstItems.SelectedItem > _lstItems.Source.Count - 1) {
_lstItems.SelectedItem = _lstItems.Source.Count - 1;
}
_lstItems.SetNeedsDisplay ();
SetFrameDetails ();
}
};
_lstItems.Enter += (s, e) => {
var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [_lstItems.SelectedItem].StatusItem : null;
SetFrameDetails (statusItem);
};
_btnAddStatusBar.Clicked += (s, e) => {
if (_statusBar != null) {
return;
}
_statusBar = new StatusBar ();
Add (_statusBar);
};
_btnRemoveStatusBar.Clicked += (s, e) => {
if (_statusBar == null) {
return;
}
Remove (_statusBar);
_statusBar = null;
DataContext.Items = new List<DynamicStatusItemList> ();
_currentStatusItem = null;
_currentSelectedStatusBar = -1;
SetListViewSource (_currentStatusItem, true);
SetFrameDetails (null);
};
SetFrameDetails ();
var ustringConverter = new UStringValueConverter ();
var listWrapperConverter = new ListWrapperConverter ();
var lstItems = new Binding (this, "Items", _lstItems, "Source", listWrapperConverter);
void SetFrameDetails (StatusItem statusItem = null)
{
StatusItem newStatusItem;
if (statusItem == null) {
newStatusItem = DataContext.Items.Count > 0 ? DataContext.Items [_lstItems.SelectedItem].StatusItem : null;
} else {
newStatusItem = statusItem;
}
_currentEditStatusItem = newStatusItem;
_frmStatusBarDetails.EditStatusItem (newStatusItem);
var f = _btnOk.Enabled == _frmStatusBarDetails.Enabled;
if (!f) {
_btnOk.Enabled = _frmStatusBarDetails.Enabled;
_btnCancel.Enabled = _frmStatusBarDetails.Enabled;
}
}
void SetListViewSource (StatusItem _currentStatusItem, bool fill = false)
{
DataContext.Items = new List<DynamicStatusItemList> ();
var statusItem = _currentStatusItem;
if (!fill) {
return;
}
if (statusItem != null) {
foreach (var si in _statusBar.Items) {
DataContext.Items.Add (new DynamicStatusItemList (si.Title, si));
}
}
}
StatusItem CreateNewStatusBar (DynamicStatusItem item)
{
var newStatusItem = new StatusItem (ShortcutHelper.GetShortcutFromTag (
item.shortcut, StatusBar.ShortcutDelimiter),
item.title, _frmStatusBarDetails.CreateAction (item));
return newStatusItem;
}
void UpdateStatusItem (StatusItem _currentEditStatusItem, DynamicStatusItem statusItem, int index)
{
_currentEditStatusItem = CreateNewStatusBar (statusItem);
_statusBar.Items [index] = _currentEditStatusItem;
if (DataContext.Items.Count == 0) {
DataContext.Items.Add (new DynamicStatusItemList (_currentEditStatusItem.Title, _currentEditStatusItem));
}
DataContext.Items [index] = new DynamicStatusItemList (_currentEditStatusItem.Title, _currentEditStatusItem);
SetFrameDetails (_currentEditStatusItem);
}
//_frmStatusBarDetails.Initialized += (s, e) => _frmStatusBarDetails.Enabled = false;
}
public static string SetTitleText (string title, string shortcut)
{
var txt = title;
var split = title.Split ('~');
if (split.Length > 1) {
txt = split [2].Trim (); ;
}
if (string.IsNullOrEmpty (shortcut)) {
return txt;
}
return $"~{shortcut}~ {txt}";
}
}
public class DynamicStatusBarDetails : FrameView {
public StatusItem _statusItem;
public TextField _txtTitle;
public TextView _txtAction;
public TextField _txtShortcut;
public DynamicStatusBarDetails (StatusItem statusItem = null) : this (statusItem == null ? "Adding New StatusBar Item." : "Editing StatusBar Item.")
{
_statusItem = statusItem;
}
public DynamicStatusBarDetails (string title) : base (title)
{
var _lblTitle = new Label ("Title:") {
Y = 1
};
Add (_lblTitle);
_txtTitle = new TextField () {
X = Pos.Right (_lblTitle) + 4,
Y = Pos.Top (_lblTitle),
Width = Dim.Fill ()
};
Add (_txtTitle);
var _lblAction = new Label ("Action:") {
X = Pos.Left (_lblTitle),
Y = Pos.Bottom (_lblTitle) + 1
};
Add (_lblAction);
_txtAction = new TextView () {
X = Pos.Left (_txtTitle),
Y = Pos.Top (_lblAction),
Width = Dim.Fill (),
Height = 5
};
Add (_txtAction);
var _lblShortcut = new Label ("Shortcut:") {
X = Pos.Left (_lblTitle),
Y = Pos.Bottom (_txtAction) + 1
};
Add (_lblShortcut);
_txtShortcut = new TextField () {
X = Pos.X (_txtAction),
Y = Pos.Y (_lblShortcut),
Width = Dim.Fill (),
ReadOnly = true
};
_txtShortcut.KeyDown += (s, e) => {
if (!ProcessKey (e)) {
return;
}
if (CheckShortcut (e.KeyCode, true)) {
e.Handled = true;
}
};
bool ProcessKey (Key ev)
{
switch (ev.KeyCode) {
case KeyCode.CursorUp:
case KeyCode.CursorDown:
case KeyCode.Tab:
case KeyCode.Tab | KeyCode.ShiftMask:
return false;
}
return true;
}
bool CheckShortcut (KeyCode k, bool pre)
{
var m = _statusItem != null ? _statusItem : new StatusItem (k, "", null);
if (pre && !ShortcutHelper.PreShortcutValidation (k)) {
_txtShortcut.Text = "";
return false;
}
if (!pre) {
if (!ShortcutHelper.PostShortcutValidation (ShortcutHelper.GetShortcutFromTag (
_txtShortcut.Text, StatusBar.ShortcutDelimiter))) {
_txtShortcut.Text = "";
return false;
}
return true;
}
_txtShortcut.Text = Key.ToString (k, StatusBar.ShortcutDelimiter);//ShortcutHelper.GetShortcutTag (k, StatusBar.ShortcutDelimiter);
return true;
}
_txtShortcut.KeyUp += (s, e) => {
if (CheckShortcut (e.KeyCode, true)) {
e.Handled = true;
}
};
Add (_txtShortcut);
var _btnShortcut = new Button ("Clear Shortcut") {
X = Pos.X (_lblShortcut),
Y = Pos.Bottom (_txtShortcut) + 1
};
_btnShortcut.Clicked += (s, e) => {
_txtShortcut.Text = "";
};
Add (_btnShortcut);
}
public DynamicStatusItem EnterStatusItem ()
{
var valid = false;
if (_statusItem == null) {
var m = new DynamicStatusItem ();
_txtTitle.Text = m.title;
_txtAction.Text = m.action;
} else {
EditStatusItem (_statusItem);
}
var _btnOk = new Button ("Ok") {
IsDefault = true,
};
_btnOk.Clicked += (s, e) => {
if (string.IsNullOrEmpty (_txtTitle.Text)) {
MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
} else {
if (!string.IsNullOrEmpty (_txtShortcut.Text)) {
_txtTitle.Text = DynamicStatusBarSample.SetTitleText (
_txtTitle.Text, _txtShortcut.Text);
}
valid = true;
Application.RequestStop ();
}
};
var _btnCancel = new Button ("Cancel");
_btnCancel.Clicked += (s, e) => {
_txtTitle.Text = string.Empty;
Application.RequestStop ();
};
var _dialog = new Dialog (_btnOk, _btnCancel) { Title = "Enter the menu details." };
Width = Dim.Fill ();
Height = Dim.Fill () - 1;
_dialog.Add (this);
_txtTitle.SetFocus ();
_txtTitle.CursorPosition = _txtTitle.Text.Length;
Application.Run (_dialog);
if (valid) {
return new DynamicStatusItem (_txtTitle.Text, _txtAction.Text, _txtShortcut.Text);
} else {
return null;
}
}
public void EditStatusItem (StatusItem statusItem)
{
if (statusItem == null) {
Enabled = false;
CleanEditStatusItem ();
return;
} else {
Enabled = true;
}
_statusItem = statusItem;
_txtTitle.Text = statusItem?.Title ?? "";
_txtAction.Text = statusItem != null && statusItem.Action != null ? GetTargetAction (statusItem.Action) : string.Empty;
_txtShortcut.Text = Key.ToString ((KeyCode)statusItem.Shortcut, StatusBar.ShortcutDelimiter);//ShortcutHelper.GetShortcutTag (statusItem.Shortcut, StatusBar.ShortcutDelimiter) ?? "";
}
void CleanEditStatusItem ()
{
_txtTitle.Text = "";
_txtAction.Text = "";
_txtShortcut.Text = "";
}
string GetTargetAction (Action action)
{
var me = action.Target;
if (me == null) {
throw new ArgumentException ();
}
object v = new object ();
foreach (var field in me.GetType ().GetFields ()) {
if (field.Name == "item") {
v = field.GetValue (me);
}
}
return v == null || !(v is DynamicStatusItem item) ? string.Empty : item.action;
}
public Action CreateAction (DynamicStatusItem item)
{
return new Action (() => MessageBox.ErrorQuery (item.title, item.action, "Ok"));
}
}
public class DynamicStatusItemModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string statusBar;
private List<DynamicStatusItemList> items;
public string StatusBar {
get => statusBar;
set {
if (value != statusBar) {
statusBar = value;
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (GetPropertyName ()));
}
}
}
public List<DynamicStatusItemList> Items {
get => items;
set {
if (value != items) {
items = value;
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (GetPropertyName ()));
}
}
}
public DynamicStatusItemModel ()
{
Items = new List<DynamicStatusItemList> ();
}
public string GetPropertyName ([CallerMemberName] string propertyName = null)
{
return propertyName;
}
}
public interface IValueConverter {
object Convert (object value, object parameter = null);
}
public class Binding {
public View Target { get; private set; }
public View Source { get; private set; }
public string SourcePropertyName { get; private set; }
public string TargetPropertyName { get; private set; }
private object sourceDataContext;
private PropertyInfo sourceBindingProperty;
private IValueConverter valueConverter;
public Binding (View source, string sourcePropertyName, View target, string targetPropertyName, IValueConverter valueConverter = null)
{
Target = target;
Source = source;
SourcePropertyName = sourcePropertyName;
TargetPropertyName = targetPropertyName;
sourceDataContext = Source.GetType ().GetProperty ("DataContext").GetValue (Source);
sourceBindingProperty = sourceDataContext.GetType ().GetProperty (SourcePropertyName);
this.valueConverter = valueConverter;
UpdateTarget ();
var notifier = ((INotifyPropertyChanged)sourceDataContext);
if (notifier != null) {
notifier.PropertyChanged += (s, e) => {
if (e.PropertyName == SourcePropertyName) {
UpdateTarget ();
}
};
}
}
private void UpdateTarget ()
{
try {
var sourceValue = sourceBindingProperty.GetValue (sourceDataContext);
if (sourceValue == null) {
return;
}
var finalValue = valueConverter?.Convert (sourceValue) ?? sourceValue;
var targetProperty = Target.GetType ().GetProperty (TargetPropertyName);
targetProperty.SetValue (Target, finalValue);
} catch (Exception ex) {
MessageBox.ErrorQuery ("Binding Error", $"Binding failed: {ex}.", "Ok");
}
}
}
public class ListWrapperConverter : IValueConverter {
public object Convert (object value, object parameter = null)
{
return new ListWrapper ((IList)value);
}
}
public class UStringValueConverter : IValueConverter {
public object Convert (object value, object parameter = null)
{
var data = Encoding.ASCII.GetBytes (value.ToString ());
return StringExtensions.ToString (data);
}
}
}