diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-09-15 19:26:39 +0000 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-09-15 19:26:39 +0000 |
| commit | 8b343e10f1a65b07988e15ea76596b16a03adadc (patch) | |
| tree | fc12eee11de644d429e6fb67516c46be82669377 | |
| parent | 5dcfd372592d5d21193d1dc5a58c385b2e8a6a7c (diff) | |
hosted command
28 files changed, 446 insertions, 275 deletions
diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index 3b054bb4..1c61b00e 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -3,10 +3,10 @@ <PropertyGroup> <!-- <TargetFrameworks>netstandard2.0</TargetFrameworks>--> <TargetFramework>netstandard2.1</TargetFramework> - + <AssemblyVersion>$(CrowVersion)</AssemblyVersion> <PackageVersion>$(CrowPackageVersion)</PackageVersion> - + <Title>C# Rapid Open Widget Toolkit</Title> <Description>C.R.O.W. is a widget toolkit and rendering engine developed in C# with templates, styles, compositing, and bindings.</Description> <License>MIT</License> @@ -23,7 +23,7 @@ <GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GenerateDocumentationFile>true</GenerateDocumentationFile> <NoWarn>$(NoWarn);1591;1587;1570;1572;1573;1574</NoWarn> - <DefineConstants>MEASURE_TIME;_DEBUG_HIGHLIGHT_FOCUS</DefineConstants> + <DefineConstants>MEASURE_TIME;_DEBUG_HIGHLIGHT_FOCUS</DefineConstants> <EnableDefaultItems>false</EnableDefaultItems> <EnableDefaultCompileItems>false</EnableDefaultCompileItems> <!--<AllowUnsafeBlocks>true</AllowUnsafeBlocks>--> @@ -40,7 +40,7 @@ </ItemGroup> <ItemGroup> <!--<PackageReference Include="FastEnum" Version="1.5.3" />--> - <PackageReference Include="Enums.NET" Version="4.0.0" /> + <PackageReference Include="Enums.NET" Version="4.0.0" /> <PackageReference Include="glfw-sharp" Version="$(GlfwSharpVersion)" /> </ItemGroup> diff --git a/Crow/src/Command/ActionCommand.cs b/Crow/src/Command/ActionCommand.cs index 02bec998..f484e0ed 100644 --- a/Crow/src/Command/ActionCommand.cs +++ b/Crow/src/Command/ActionCommand.cs @@ -39,9 +39,22 @@ namespace Crow { { execute = executeAction; } + public ActionCommand (ICommandHost _host, string caption, Action executeAction, string icon = null, KeyBinding _keyBinding = null, + Binding<bool> _canExecuteBinding = null) + : base (_host, caption, icon, _keyBinding, _canExecuteBinding) + { + execute = executeAction; + } + public ActionCommand (ICommandHost _host, string caption, Action<object> executeAction, string icon = null, KeyBinding _keyBinding = null, + Binding<bool> _canExecuteBinding = null) + : base (_host, caption, icon, _keyBinding, _canExecuteBinding) + { + execute = executeAction; + } + #endregion - Delegate execute; + protected Delegate execute; /// <summary> /// trigger the execution of the command @@ -56,4 +69,14 @@ namespace Crow { } } } + public class ActionCommand<T> : ActionCommand { + public ActionCommand (Action<T> _executeAction) { + execute = _executeAction; + } + public ActionCommand (string caption, Action<T> executeAction, string icon = null, bool _canExecute = true) + : base (caption, default(Action), icon, _canExecute) + { + execute = executeAction; + } + } } diff --git a/Crow/src/Command/Binding.cs b/Crow/src/Command/Binding.cs new file mode 100644 index 00000000..4437e98e --- /dev/null +++ b/Crow/src/Command/Binding.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2021-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Reflection; +using System.Linq; +using EnumsNET; +using Glfw; +using System.Reflection.Emit; + +namespace Crow +{ + public class Binding<T> { + public string SourceMember; + public Func<T> FetchFunc; + + public Binding (string memberName, Func<T> fetchFunc = null) { + SourceMember = memberName; + FetchFunc = fetchFunc; + } + public T Get (object instance) { + MemberInfo mbi = instance.GetType().GetMember (SourceMember, BindingFlags.Instance | BindingFlags.Public ).FirstOrDefault(); + if (mbi is PropertyInfo pi) + return (T)pi.GetGetMethod ().Invoke (instance, null); + else if (mbi is FieldInfo fi) + return (T)fi.GetValue (instance); + else if (mbi is MethodInfo mi) + return (T)mi.Invoke (instance, null); + else + throw new Exception ($"unsupported member type for Binding<T>: {mbi.MemberType}"); + } + public Func<T> CreateGetter (object instance) { + MemberInfo mbi = instance.GetType().GetMember (SourceMember, BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(); + if (mbi is PropertyInfo pi) { + return (Func<T>)Delegate.CreateDelegate (typeof (Func<T>), instance, pi.GetGetMethod ()); + } else if (mbi is FieldInfo fi) { + DynamicMethod dm = new DynamicMethod($"{fi.ReflectedType.FullName}_fldget", typeof(T), new Type[] { fi.ReflectedType }, false); + ILGenerator il = dm.GetILGenerator (); + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Ldfld, fi); + + il.Emit(OpCodes.Ret); + + return (Func<T>)dm.CreateDelegate (typeof (Func<T>), instance); + } else + throw new Exception ("unsupported member type for Binding.CreateGetter"); + } + public Action<T> CreateSetter (object instance) { + MemberInfo mbi = instance.GetType().GetMember (SourceMember, BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(); + if (mbi is PropertyInfo pi) { + return (Action<T>)Delegate.CreateDelegate (typeof (Action<T>), instance, pi.GetSetMethod ()); + } else if (mbi is FieldInfo fi) { + DynamicMethod dm = new DynamicMethod($"{fi.ReflectedType.FullName}_fldset", null, new Type[] { fi.ReflectedType, typeof(T)}, false); + ILGenerator il = dm.GetILGenerator (); + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Stfld, fi); + + il.Emit(OpCodes.Ret); + + return (Action<T>)dm.CreateDelegate (typeof (Action<T>), instance); + } else + throw new Exception ("unsupported member type for Binding CreateSetter"); + } + } +}
\ No newline at end of file diff --git a/Crow/src/Command/Command.cs b/Crow/src/Command/Command.cs index f44fe5b3..85c44413 100644 --- a/Crow/src/Command/Command.cs +++ b/Crow/src/Command/Command.cs @@ -7,20 +7,56 @@ using System.ComponentModel; using System.Threading.Tasks; namespace Crow { + + public interface ICommandHost : IValueChange + { + event EventHandler<KeyEventArgs> KeyDown; + } /// <summary> /// Base abstract class for commands with an execute method. /// </summary> - public abstract class Command : CommandBase + public abstract class Command : CommandBase, IDisposable { #region CTOR public Command () {} - public Command (string caption, string icon = null, bool _canExecute = true) + public Command (string caption, string icon = null, bool _canExecute = true ) :base (caption, icon) - {} + { + CanExecute = _canExecute; + } + public Command (ICommandHost _host, string caption, string icon, KeyBinding _keyBinding = null, + bool _canExecute = true) + : this (_host, caption, icon, _keyBinding, null) + { + CanExecute = _canExecute; + } + + public Command (ICommandHost _host, string caption, string icon, KeyBinding _keyBinding = null, + Binding<bool> _canExecuteBinding = null) + : base (caption, icon) + { + host = _host; + keyBinding = _keyBinding; + canExecuteBinding = _canExecuteBinding; + + if (HasKeyBinding) + host.KeyDown += key_handler; + if (HasCanExecuteBinding) { + host.ValueChanged += canExecuteBinding_handler; + CanExecute = canExecuteBinding.Get (host); + } + } #endregion + protected ICommandHost host; bool canExecute = true; + KeyBinding keyBinding; + Binding<bool> canExecuteBinding; + + public KeyBinding KeyBinding => keyBinding; + public bool HasKeyBinding => keyBinding != null; + public bool HasCanExecuteBinding => canExecuteBinding != null; /// <summary> /// if true, command may be executed, @@ -46,5 +82,34 @@ namespace Crow { base.raiseAllValuesChanged(); NotifyValueChanged ("CanExecute", CanExecute); } + void key_handler (object sender, KeyEventArgs e) { + if (CanExecute && e.Key == keyBinding.Key && e.Modifiers == keyBinding.Modifiers) { + Execute (sender); + e.Handled = true; + } + } + void canExecuteBinding_handler (object sender, ValueChangeEventArgs e) { + if (e.MemberName == canExecuteBinding.SourceMember) + CanExecute = (bool)e.NewValue; + } + #region IDispose implementation + protected bool disposed; + protected virtual void Dispose(bool disposing) + { + if (disposing && !disposed && host != null) { + if (HasKeyBinding) + host.KeyDown -= key_handler; + if (HasCanExecuteBinding) + host.ValueChanged -= canExecuteBinding_handler; + } + disposed = true; + } + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + #endregion } } diff --git a/Crow/src/Command/CommandGroup.cs b/Crow/src/Command/CommandGroup.cs index d323ae85..6c5a76ba 100644 --- a/Crow/src/Command/CommandGroup.cs +++ b/Crow/src/Command/CommandGroup.cs @@ -9,7 +9,8 @@ using System.Linq; namespace Crow { public class CommandGroup : CommandBase, IEnumerable, IList<CommandBase> { - public IList<CommandBase> Commands; + public IList<CommandBase> Commands { get; private set; } + public CommandGroup () { Commands = new ObservableList<CommandBase>(); @@ -25,6 +26,9 @@ namespace Crow { public CommandGroup (params CommandBase[] commands) { Commands = new ObservableList<CommandBase>(commands); } + public CommandGroup (ICommandHost host) { + + } public int Count => Commands.Count; @@ -61,7 +65,10 @@ namespace Crow { foreach (CommandBase c in items) Commands.Remove (c); } - /// + /// <summary> + /// Set boolean value for the CanExecute state of all commands + /// </summary> + /// <param name="canExecute"></param> public void ToggleAllCommand (bool canExecute) { foreach (ActionCommand c in Commands.OfType<ActionCommand> ()) c.CanExecute = canExecute; diff --git a/Crow/src/Command/KeyBinding.cs b/Crow/src/Command/KeyBinding.cs new file mode 100644 index 00000000..2c854048 --- /dev/null +++ b/Crow/src/Command/KeyBinding.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2021-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using EnumsNET; +using Glfw; + +namespace Crow +{ + public class KeyBinding + { + public readonly Key Key; + public readonly Modifier Modifiers; + public KeyBinding (Key key, Modifier modifiers = 0) { + Key = key; + Modifiers = modifiers; + } + public KeyBinding Parse (string str) { + string[] tmp = str.Split (','); + if (Enums.TryParse<Key> (tmp[0], out Key k)) { + if (tmp.Length > 1 && FlagEnums.TryParseFlags<Modifier> (tmp[1], out Modifier mods)) + return new KeyBinding (k, mods); + else + return new KeyBinding (k); + } + return default; + } + public override string ToString () => $"{FlagEnums.FormatFlags (Modifiers, "+")} + {Key}"; + } +}
\ No newline at end of file diff --git a/Crow/src/Command/ToggleCommand.cs b/Crow/src/Command/ToggleCommand.cs index 6182195c..45032a22 100644 --- a/Crow/src/Command/ToggleCommand.cs +++ b/Crow/src/Command/ToggleCommand.cs @@ -13,54 +13,33 @@ namespace Crow { /// <summary> /// helper class to bind in one step icon, caption, action, and validity tests to a controls /// </summary> - public class ToggleCommand : Command, IToggle, IDisposable + public class ToggleCommand : Command, IToggle { #region CTOR - public ToggleCommand () {} - public ToggleCommand (object instance, string memberName, string icon = null, bool _canExecute = true) - : this ("", instance, memberName, icon, _canExecute) { } - public ToggleCommand (string caption, object instance, string memberName, string icon = null, bool _canExecute = true) - : base (caption, icon, _canExecute) + public ToggleCommand (ICommandHost _host, string caption, Binding<bool> toggleBinding, string icon = null, KeyBinding _keyBinding = null, + Binding<bool> _canExecuteBinding = null) + : base (_host, caption, icon, _keyBinding, _canExecuteBinding) { - this.instance = instance; - this.memberName = memberName; - MemberInfo mbi = instance.GetType().GetMember (memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(); - - if (mbi is PropertyInfo pi) { - delSet = (Action<bool>)Delegate.CreateDelegate (typeof (Action<bool>), instance, pi.GetSetMethod ()); - delGet = (Func<bool>)Delegate.CreateDelegate (typeof (Func<bool>), instance, pi.GetGetMethod ()); - } else if (mbi is FieldInfo fi) { - DynamicMethod dm = new DynamicMethod($"{fi.ReflectedType.FullName}_fldset", null, new Type[] { fi.ReflectedType, typeof(bool) }, false); - ILGenerator il = dm.GetILGenerator (); - il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Stfld, fi); - - il.Emit(OpCodes.Ret); - - delSet = (Action<bool>)dm.CreateDelegate (typeof (Action<bool>), instance); - - dm = new DynamicMethod($"{fi.ReflectedType.FullName}_fldget", typeof(bool), new Type[] { fi.ReflectedType }, false); - il = dm.GetILGenerator (); - il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, fi); - - il.Emit(OpCodes.Ret); - - delGet = (Func<bool>)dm.CreateDelegate (typeof (Func<bool>), instance); - } else - throw new Exception ("unsupported member type for ToggleCommand"); - - if (instance is IValueChange ivc) - ivc.ValueChanged += instance_valueChanged; + binding = toggleBinding; + delSet = binding.CreateSetter (host); + delGet = binding.CreateGetter (host); + host.ValueChanged += toggleCommand_host_valueChanged_handler; + } + public ToggleCommand (ICommandHost _host, string caption, Binding<bool> toggleBinding, string icon = null, + KeyBinding _keyBinding = null, bool _canExecute = true) + : this (_host, caption, toggleBinding, icon, _keyBinding, null) + { CanExecute = _canExecute; } - void instance_valueChanged (object sender, ValueChangeEventArgs e) { - if (e.MemberName != memberName) - return; - //Console.WriteLine ($"ToggleCommand valueChanged triggered => {e.NewValue}"); + #endregion + Binding<bool> binding; + Action<bool> delSet; + Func<bool> delGet; + void toggleCommand_host_valueChanged_handler (object sender, ValueChangeEventArgs e) { + if (e.MemberName != binding.SourceMember) + return; bool tog = (bool)e.NewValue; NotifyValueChanged ("IsToggled", tog); if (tog) @@ -69,12 +48,6 @@ namespace Crow { ToggleOff.Raise (this, null); } - #endregion - object instance; - string memberName; - Action<bool> delSet; - Func<bool> delGet; - bool disposedValue; /// <summary> @@ -92,7 +65,6 @@ namespace Crow { } #region IToggle implementation - public event EventHandler ToggleOn; public event EventHandler ToggleOff; public BooleanTestOnInstance IsToggleable {get; set; } @@ -113,24 +85,13 @@ namespace Crow { } #endregion - protected virtual void Dispose(bool disposing) + #region IDispose implementation + protected override void Dispose(bool disposing) { - if (!disposedValue) - { - if (disposing) - { - if (instance is IValueChange ivc) - ivc.ValueChanged -= instance_valueChanged; - } - disposedValue = true; - } - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); + if (disposing && !disposed && host != null) + host.ValueChanged -= toggleCommand_host_valueChanged_handler; + base.Dispose (true); } + #endregion } } diff --git a/Crow/src/DebugUtils/PerformanceMeasure.cs b/Crow/src/DebugUtils/PerformanceMeasure.cs index 8cd72d97..6fa2ad2d 100644 --- a/Crow/src/DebugUtils/PerformanceMeasure.cs +++ b/Crow/src/DebugUtils/PerformanceMeasure.cs @@ -12,7 +12,7 @@ namespace Crow public event EventHandler<ValueChangeEventArgs> ValueChanged; public virtual void NotifyValueChanged(string MemberName, object _value) { - if (ValueChanged != null) + if (ValueChanged != null) ValueChanged.Invoke(this, new ValueChangeEventArgs(MemberName, _value)); } #endregion @@ -23,7 +23,7 @@ namespace Crow Layouting, Drawing } - public static PerformanceMeasure[] Measures; + public static PerformanceMeasure[] Measures; [Conditional("MEASURE_TIME")] public static void InitMeasures () { @@ -36,7 +36,7 @@ namespace Crow [Conditional ("MEASURE_TIME")] public static void Notify () { for (int i = 0; i < 4; i++) - Measures[i].NotifyChanges (); + Measures[i].NotifyChanges (); } [Conditional ("MEASURE_TIME")] public static void Begin (Kind kind) { @@ -80,7 +80,7 @@ namespace Crow } } - void computeStats(){ + void computeStats(){ current = timer.ElapsedMilliseconds; if (current < cancelLimit) return; @@ -89,7 +89,7 @@ namespace Crow if (timer.ElapsedMilliseconds < minimum) minimum = timer.ElapsedMilliseconds; if (timer.ElapsedMilliseconds > maximum) - maximum = timer.ElapsedMilliseconds; + maximum = timer.ElapsedMilliseconds; } void ResetStats(){ Debug.WriteLine("reset measure cpt:{0}",cptMeasures); diff --git a/Crow/src/Enums.cs b/Crow/src/Enums.cs index ebbb8d22..866e32a9 100644 --- a/Crow/src/Enums.cs +++ b/Crow/src/Enums.cs @@ -10,7 +10,7 @@ namespace Crow Vertical } - public enum Alignment + public enum Alignment { Top = 0x01, Left = 0x02, @@ -106,7 +106,7 @@ namespace Crow ur_angle, watch, X_cursor, - xterm, + xterm, } /// <summary> /// Cursor shape use in Sliders diff --git a/Crow/src/EventArgs/ListChangedEventArg.cs b/Crow/src/EventArgs/ListChangedEventArg.cs index 2d8553b8..7f83cfaf 100644 --- a/Crow/src/EventArgs/ListChangedEventArg.cs +++ b/Crow/src/EventArgs/ListChangedEventArg.cs @@ -19,7 +19,7 @@ namespace Crow public class ListClearEventArg : EventArgs { public IEnumerable<object> Elements; - public ListClearEventArg (IEnumerable<object> elements) { + public ListClearEventArg (IEnumerable<object> elements) { Elements = elements; } } diff --git a/Crow/src/Fill/Gradient.cs b/Crow/src/Fill/Gradient.cs index 7fe3d4c1..2d344c73 100644 --- a/Crow/src/Fill/Gradient.cs +++ b/Crow/src/Fill/Gradient.cs @@ -23,7 +23,7 @@ namespace Crow public double Offset; public Color Color; - public ColorStop(double offset, Color color){ + public ColorStop(double offset, Color color){ Offset = offset; Color = color; } @@ -31,7 +31,7 @@ namespace Crow { if (string.IsNullOrEmpty (s)) return null; - + string[] parts = s.Trim ().Split (':'); if (parts.Length > 2) @@ -80,7 +80,7 @@ namespace Crow continue; grad.AddColorStop (cs.Offset, cs.Color); } - + ctx.SetSource (grad); grad.Dispose (); } diff --git a/Crow/src/IML/CompilerServices.cs b/Crow/src/IML/CompilerServices.cs index 3daf82ab..69c74e42 100644 --- a/Crow/src/IML/CompilerServices.cs +++ b/Crow/src/IML/CompilerServices.cs @@ -778,6 +778,7 @@ namespace Crow.IML eiEvt.RemoveEventHandler (instance, d); #if DEBUG_BINDING Console.WriteLine ("\tremoveEventHandlerByName: {0} handler removed in {1} for: {2}", d.Method.Name,instance, eventName); + #endif } } diff --git a/Crow/src/Input/KeyEventArgs.cs b/Crow/src/Input/KeyEventArgs.cs index 23f8559d..30605e78 100644 --- a/Crow/src/Input/KeyEventArgs.cs +++ b/Crow/src/Input/KeyEventArgs.cs @@ -8,33 +8,34 @@ using Glfw; namespace Crow { public class KeyEventArgs : CrowEventArgs - { - int keyCode=0; + { Key key; - bool repeat; + int scancode; + Modifier modifiers; + bool repeat; - public KeyEventArgs(Key _key, bool _repeat) + public KeyEventArgs (Key _key, bool _repeat = false) { key = _key; repeat = _repeat; } - public KeyEventArgs(KeyEventArgs args) + public KeyEventArgs (Key _key, int _scancode, Modifier _modifiers, bool _repeat = false) { - Key = args.Key; - } - public Key Key - { - get { return key; } - internal set { key = value; } - } - public uint ScanCode - { - get { return (uint)keyCode; } + key = _key; + scancode = _scancode; + modifiers = _modifiers; + repeat = _repeat; } - public bool IsRepeat + public KeyEventArgs (KeyEventArgs e) { - get { return repeat; } - internal set { repeat = value; } + key = e.Key; + repeat = e.IsRepeat; + scancode = e.ScanCode; + modifiers = e.Modifiers; } - } + public Key Key => key; + public int ScanCode => scancode; + public Modifier Modifiers => modifiers; + public bool IsRepeat => repeat; + } } diff --git a/Crow/src/Input/MouseEventArgs.cs b/Crow/src/Input/MouseEventArgs.cs index 9d3badda..f84822eb 100644 --- a/Crow/src/Input/MouseEventArgs.cs +++ b/Crow/src/Input/MouseEventArgs.cs @@ -26,7 +26,7 @@ namespace Crow public class MouseEventArgs : CrowEventArgs { public readonly int X, Y; - public Point Position => new Point (X, Y); + public Point Position => new Point (X, Y); public MouseEventArgs () { } public MouseEventArgs (int x, int y) { X = x; @@ -44,7 +44,7 @@ namespace Crow } } - public class MouseButtonEventArgs : MouseEventArgs + public class MouseButtonEventArgs : MouseEventArgs { public readonly MouseButton Button; public readonly InputAction Action; diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index b46bf018..ede2927e 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -45,7 +45,7 @@ namespace Crow /// The resulting surface (a byte array in the OpenTK renderer) is made available and protected with the /// RenderMutex of the interface. /// </remarks> - public class Interface : ILayoutable, IDisposable, IValueChange + public class Interface : ILayoutable, IDisposable, ICommandHost { #region IValueChange implementation public event EventHandler<ValueChangeEventArgs> ValueChanged; @@ -401,12 +401,17 @@ namespace Crow windows [window].OnMouseWheelChanged ((int)yOffset); }; static KeyDelegate HandleKeyDelegate = (IntPtr window, Key key, int scanCode, InputAction action, Modifier modifiers) => { + string localizedKey = Glfw3.GetKeyName (key, scanCode); + if (!string.IsNullOrEmpty (localizedKey) && EnumsNET.Enums.TryParse<Key> (localizedKey, true, out Key locKey)) + key = locKey; + if (action == InputAction.Release) - windows [window].OnKeyUp (key); + windows [window].OnKeyUp (new KeyEventArgs (key, scanCode, modifiers)); else - windows [window].OnKeyDown (key); + windows [window].OnKeyDown (new KeyEventArgs (key, scanCode, modifiers)); }; static CharDelegate HandleCharDelegate = (IntPtr window, CodePoint codepoint) => { + Console.WriteLine ($"Char: cp:{codepoint.Value} -> '{codepoint}'"); windows [window].OnKeyPress (codepoint.ToChar()); }; static WindowSizeDelegate HandleWindowSizeDelegate = (IntPtr window, int Width, int Height) => { @@ -604,13 +609,15 @@ namespace Crow public event EventHandler StartDragOperation; public event EventHandler EndDragOperation; + public event EventHandler<KeyEventArgs> KeyDown; + public event EventHandler<KeyEventArgs> KeyUp; + //public event EventHandler<MouseWheelEventArgs> MouseWheelChanged; //public event EventHandler<MouseButtonEventArgs> MouseButtonUp; //public event EventHandler<MouseButtonEventArgs> MouseButtonDown; //public event EventHandler<MouseButtonEventArgs> MouseClick; //public event EventHandler<MouseMoveEventArgs> MouseMove; //public event EventHandler<KeyEventArgs> KeyDown; - //public event EventHandler<KeyEventArgs> KeyUp; //public event EventHandler<KeyPressEventArgs> KeyPress; /*public event EventHandler<KeyEventArgs> KeyboardKeyDown; public event EventHandler<KeyEventArgs> KeyboardKeyUp;*/ @@ -1865,26 +1872,22 @@ namespace Crow public virtual bool OnKeyPress (char c) { - if (_focusedWidget == null) - return false; - _focusedWidget.onKeyPress (_focusedWidget, new KeyPressEventArgs (c)); - return true; + KeyPressEventArgs e = new KeyPressEventArgs (c); + if (_focusedWidget != null) + _focusedWidget.onKeyPress (_focusedWidget, e); + return e.Handled; } - public virtual bool OnKeyUp (Key key) + public virtual bool OnKeyUp (KeyEventArgs e) { - if (_focusedWidget == null) - return false; - _focusedWidget.onKeyUp (_focusedWidget, new KeyEventArgs (key, false)); - return true; - - - // if (keyboardRepeatThread != null) { - // keyboardRepeatOn = false; - // keyboardRepeatThread.Abort(); - // keyboardRepeatThread.Join (); - // } + if (!e.Handled) { + if (KeyUp != null) + KeyUp.Invoke (this, e); + if (!e.Handled && _focusedWidget != null) + _focusedWidget.onKeyUp (_focusedWidget, e); + } + return e.Handled; } - public virtual bool OnKeyDown (Key key) + public virtual bool OnKeyDown (KeyEventArgs e) { #if DEBUG_STATS if (Shift && key == Key.F1) { @@ -1912,18 +1915,15 @@ namespace Crow ").DataSource = this; } #endif + lastKeyDownEvt = new KeyEventArgs (e); - //Keyboard.SetKeyState((Crow.Key)Key,true); - lastKeyDownEvt = new KeyEventArgs (key, true); - - if (_focusedWidget == null) - return false; - _focusedWidget.onKeyDown (_focusedWidget, new KeyEventArgs (key, false)); - return true; - - // keyboardRepeatThread = new Thread (keyboardRepeatThreadFunc); - // keyboardRepeatThread.IsBackground = true; - // keyboardRepeatThread.Start (); + if (!e.Handled) { + if (KeyDown != null) + KeyDown.Invoke (this, e); + if (!e.Handled && _focusedWidget != null) + _focusedWidget.onKeyDown (_focusedWidget, e); + } + return e.Handled; } public bool IsKeyDown (Key key) => Glfw3.GetKey (hWin, key) == InputAction.Press; diff --git a/Crow/src/Widgets/Button.cs b/Crow/src/Widgets/Button.cs index 6c005629..a6b57687 100644 --- a/Crow/src/Widgets/Button.cs +++ b/Crow/src/Widgets/Button.cs @@ -119,11 +119,5 @@ namespace Crow } NotifyValueChanged (mName, e.NewValue); } - protected override void Dispose(bool disposing) - { - if (command is IDisposable dis) - dis.Dispose (); - base.Dispose(disposing); - } } } diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index e01fe269..9a16bf87 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -28,7 +28,7 @@ namespace Crow public event EventHandler<TextChangeEventArgs> TextChanged; public virtual void OnTextChanged(Object sender, TextChangeEventArgs e) - { + { TextChanged.Raise (this, e); } //TODO:change protected to private @@ -36,11 +36,11 @@ namespace Crow #region private and protected fields protected string _text = ""; TextAlignment _textAlignment; - bool _multiline; + bool _multiline; Color selBackground; Color selForeground; - int targetColumn = -1;//handle line changes with long->short->long line length sequence. + int targetColumn = -1;//handle line changes with long->short->long line length sequence. protected CharLocation? hoverLoc = null; protected CharLocation? currentLoc = null; @@ -127,7 +127,7 @@ namespace Crow /// <summary> /// If measure is not 'Fit', align text inside the bounds of this label. /// </summary> - [DefaultValue(TextAlignment.Left)] + [DefaultValue(TextAlignment.Left)] public TextAlignment TextAlignment { get { return _textAlignment; } @@ -142,7 +142,7 @@ namespace Crow RegisterForRedraw (); NotifyValueChangedAuto (_textAlignment); } - } + } /// <summary> /// Text to display in this label. May include linebreaks if Multiline is 'true'. /// If Multiline is false, linebreaks will be treated as unrecognized unicode char. @@ -201,12 +201,12 @@ namespace Crow RegisterForGraphicUpdate(); } } - + /// <summary> /// Moves cursor one char to the left. /// </summary> - /// <returns><c>true</c> if move succeed</returns> + /// <returns><c>true</c> if move succeed</returns> public bool MoveLeft(){ //targetColumn = -1; CharLocation loc = CurrentLoc.Value; @@ -228,7 +228,7 @@ namespace Crow } else CurrentLoc = new CharLocation (loc.Line, loc.Column + 1); return true; - } + } public bool LineMove (int lineDiff) { CharLocation loc = CurrentLoc.Value; int newLine = Math.Min (Math.Max (0, loc.Line + lineDiff), lines.Count - 1); @@ -251,7 +251,7 @@ namespace Crow } protected int visibleLines => (int)((double)ClientRectangle.Height / (fe.Ascent + fe.Descent)); public void GotoWordStart(){ - int pos = lines.GetAbsolutePosition (CurrentLoc.Value); + int pos = lines.GetAbsolutePosition (CurrentLoc.Value); //skip white spaces while (pos > 0 && !char.IsLetterOrDigit (_text[pos-1])) pos--; @@ -267,11 +267,11 @@ namespace Crow while (pos < _text.Length - 1 && char.IsLetterOrDigit (_text[pos])) pos++; CurrentLoc = lines.GetLocation (pos); - } + } protected void detectLineBreak () { if (!_multiline) - return; + return; mixedLineBreak = false; if (lines.Count == 0 || lines[0].LineBreakLength == 0) { @@ -288,8 +288,8 @@ namespace Crow } } } - - protected void getLines () { + + protected void getLines () { if (lines == null) lines = new LineCollection (_multiline ? 4 : 1); else @@ -513,7 +513,7 @@ namespace Crow } if (!CurrentLoc.Value.HasVisualX) { setFontForContext (ctx); - lock (linesMutex) { + lock (linesMutex) { if (currentLoc?.Column < 0) { updateLocation (ctx, ClientRectangle.Width, ref currentLoc); NotifyValueChanged ("CurrentColumn", CurrentColumn); @@ -534,7 +534,7 @@ namespace Crow //} Rectangle c = ScreenCoordinates (textCursor.Value + Slot.Position + ClientRectangle.Position); ctx.ResetClip (); - Foreground.SetAsSource (IFace, ctx, c); + Foreground.SetAsSource (IFace, ctx, c); ctx.LineWidth = 1.0; ctx.MoveTo (0.5 + c.X, c.Y); ctx.LineTo (0.5 + c.X, c.Bottom); @@ -613,7 +613,7 @@ namespace Crow return false; } try { - bool result = base.UpdateLayout (layoutType); + bool result = base.UpdateLayout (layoutType); return result; } finally { System.Threading.Monitor.Exit (linesMutex); @@ -635,7 +635,7 @@ namespace Crow DbgLogger.EndEvent(DbgEvtType.GOMeasure); return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width); } - + protected override void onDraw (Context gr) { DbgLogger.StartEvent(DbgEvtType.GODraw, this); @@ -643,12 +643,12 @@ namespace Crow base.onDraw (gr); setFontForContext (gr); - + if (!textMeasureIsUpToDate) { lock (linesMutex) measureTextBounds (gr); } - + if (ClipToClientRect) { gr.Save (); CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius); @@ -657,7 +657,7 @@ namespace Crow lock (linesMutex) drawContent (gr); - + if (ClipToClientRect) gr.Restore (); @@ -670,7 +670,7 @@ namespace Crow { base.onFocused (sender, e); if (CurrentLoc == null) { - selectionStart = new CharLocation (0, 0); + selectionStart = new CharLocation (0, 0); CurrentLoc = new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length); } RegisterForRedraw (); @@ -683,7 +683,7 @@ namespace Crow public override void onMouseEnter (object sender, MouseMoveEventArgs e) { base.onMouseEnter (sender, e); if (Focusable) - IFace.MouseCursor = MouseCursor.ibeam; + IFace.MouseCursor = MouseCursor.ibeam; } public override void onMouseMove (object sender, MouseMoveEventArgs e) { @@ -692,16 +692,16 @@ namespace Crow updateHoverLocation (ScreenPointToLocal (e.Position)); if (HasFocus && IFace.IsDown (MouseButton.Left)) { - CurrentLoc = hoverLoc; - RegisterForRedraw (); + CurrentLoc = hoverLoc; + RegisterForRedraw (); } - } + } public override void onMouseDown (object sender, MouseButtonEventArgs e) { if (e.Button == Glfw.MouseButton.Left) { targetColumn = -1; - if (HasFocus) { - if (!IFace.Shift) + if (HasFocus) { + if (!IFace.Shift) selectionStart = hoverLoc; else if (!selectionStart.HasValue) selectionStart = CurrentLoc; @@ -709,7 +709,7 @@ namespace Crow IFace.forceTextCursor = true; RegisterForRedraw (); e.Handled = true; - } + } } base.onMouseDown (sender, e); @@ -719,7 +719,7 @@ namespace Crow { base.onMouseUp (sender, e); if (e.Button != MouseButton.Left || !HasFocus || !selectionStart.HasValue) - return; + return; if (selectionStart.Value == CurrentLoc.Value) selectionStart = null; } @@ -730,7 +730,7 @@ namespace Crow return; GotoWordStart (); - selectionStart = CurrentLoc; + selectionStart = CurrentLoc; GotoWordEnd (); RegisterForRedraw (); } @@ -738,7 +738,7 @@ namespace Crow #region Keyboard handling public override void onKeyDown (object sender, KeyEventArgs e) { - + switch (e.Key) { case Key.Escape: selectionStart = null; @@ -794,7 +794,7 @@ namespace Crow return; } IFace.forceTextCursor = true; - e.Handled = true; + e.Handled = true; } #endregion diff --git a/Crow/src/Widgets/MenuItem.cs b/Crow/src/Widgets/MenuItem.cs index c861441f..e9160095 100644 --- a/Crow/src/Widgets/MenuItem.cs +++ b/Crow/src/Widgets/MenuItem.cs @@ -165,12 +165,6 @@ namespace Crow tmp = tmp.LogicalParent as MenuItem; } } - protected override void Dispose(bool disposing) - { - if (command is IDisposable dis) - dis.Dispose (); - base.Dispose(disposing); - } } } diff --git a/Crow/src/Widgets/PrivateContainer.cs b/Crow/src/Widgets/PrivateContainer.cs index b08e1857..b5e9fbc2 100644 --- a/Crow/src/Widgets/PrivateContainer.cs +++ b/Crow/src/Widgets/PrivateContainer.cs @@ -29,7 +29,7 @@ namespace Crow return true; if (child == null) return false; - return child.FindByDesignID (designID, out go); + return child.FindByDesignID (designID, out go); } #endif protected Widget child; @@ -59,7 +59,7 @@ namespace Crow if (child != null) { child.Parent = this; child.LayoutChanged += OnChildLayoutChanges; - contentSize = child.Slot.Size; + contentSize = child.Slot.Size; child.RegisterForLayouting (LayoutingType.Sizing); } } @@ -89,7 +89,7 @@ namespace Crow } public override bool Contains (Widget goToFind) { - return child == goToFind ? true : + return child == goToFind ? true : child == null ? false : child.Contains(goToFind); } public override void OnDataSourceChanged (object sender, DataSourceChangeEventArgs e) @@ -116,7 +116,7 @@ namespace Crow contentSize.Height = child.measureRawSize (LayoutingType.Height); break; } - DbgLogger.SetMsg(DbgEvtType.GOMeasure, $"{lt} contentSize:{contentSize}"); + DbgLogger.SetMsg(DbgEvtType.GOMeasure, $"{lt} contentSize:{contentSize}"); } return base.measureRawSize (lt); } finally { @@ -171,7 +171,7 @@ namespace Crow child.RegisterForLayouting (ltChild); } public virtual void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) - { + { Widget g = sender as Widget; if (arg.LayoutType == LayoutingType.Width) { contentSize.Width = g.Slot.Width; @@ -220,7 +220,7 @@ namespace Crow gr.Rectangle(Clipping.GetRectangle(i)); gr.ClipPreserve(); gr.Operator = Operator.Clear; - gr.Fill(); + gr.Fill(); gr.Operator = Operator.Over; onDraw (gr); @@ -237,8 +237,8 @@ namespace Crow { base.checkHoverWidget (e); - if (child != null) - if (child.MouseIsIn (e.Position)) + if (child != null) + if (child.MouseIsIn (e.Position)) child.checkHoverWidget (e); } #endregion diff --git a/Crow/src/Widgets/TextBox.cs b/Crow/src/Widgets/TextBox.cs index aebcc071..12d6ce1e 100644 --- a/Crow/src/Widgets/TextBox.cs +++ b/Crow/src/Widgets/TextBox.cs @@ -134,7 +134,7 @@ namespace Crow } /// <summary> Process scrolling vertically, or if shift is down, vertically </summary> - public override void onMouseWheel (object sender, MouseWheelEventArgs e) { + public override void onMouseWheel (object sender, MouseWheelEventArgs e) { e.Handled = true; if (IFace.Shift) ScrollX += e.Delta * MouseWheelSpeed; @@ -142,10 +142,10 @@ namespace Crow ScrollY -= e.Delta * MouseWheelSpeed; else e.Handled = false; - + base.onMouseWheel (sender, e); } - public override void onMouseMove (object sender, MouseMoveEventArgs e) { + public override void onMouseMove (object sender, MouseMoveEventArgs e) { if (!HasFocus || !IFace.IsDown (MouseButton.Left)) { base.onMouseMove (sender, e); return; @@ -215,8 +215,8 @@ namespace Crow return null; } else if (cursor.Right < 0 || cursor.X > cb.Width || cursor.Y < 0 || cursor.Bottom > cb.Height) return null; - - return cursor; + + return cursor; } void updateMaxScrolls (LayoutingType layout) { @@ -244,7 +244,7 @@ namespace Crow TextSpan selection = Selection; if (selection.IsEmpty) return; - IFace.Clipboard = SelectedText; + IFace.Clipboard = SelectedText; } public virtual void Paste () { TextSpan selection = Selection; @@ -272,8 +272,8 @@ namespace Crow if (selection.IsEmpty) { if (selection.Start == Text.Length) return; - if (CurrentLoc.Value.Column >= lines[CurrentLoc.Value.Line].Length) - update (new TextChange (selection.Start, lines[CurrentLoc.Value.Line].LineBreakLength, "")); + if (CurrentLoc.Value.Column >= lines[CurrentLoc.Value.Line].Length) + update (new TextChange (selection.Start, lines[CurrentLoc.Value.Line].LineBreakLength, "")); else update (new TextChange (selection.Start, 1, "")); } else { @@ -345,7 +345,7 @@ namespace Crow src.Slice (0, change.Start).CopyTo (tmp); change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); - + _text = tmp.ToString (); lines.Update (change); //lines.Update (_text); @@ -358,7 +358,7 @@ namespace Crow NotifyValueChanged ("Text", Text); OnTextChanged (this, new TextChangeEventArgs (change)); - + RegisterForGraphicUpdate (); } protected override string LogName => "tb"; diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 4dbcb340..0b5b4463 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -2060,19 +2060,19 @@ namespace Crow public virtual void onKeyDown(object sender, KeyEventArgs e){ if (KeyDown != null) KeyDown.Invoke (this, e); - else if (!e.Handled) + else if (!e.Handled && BubbleMouseEvent.HasFlag (DeviceEventType.KeyDown)) FocusParent?.onKeyDown (sender, e); } public virtual void onKeyUp(object sender, KeyEventArgs e){ if (KeyUp != null) KeyUp.Invoke (this, e); - else if (!e.Handled) + else if (!e.Handled && BubbleMouseEvent.HasFlag (DeviceEventType.KeyUp)) FocusParent?.onKeyUp (sender, e); } public virtual void onKeyPress(object sender, KeyPressEventArgs e){ if (KeyPress != null) KeyPress.Invoke (this, e); - else if (!e.Handled) + else if (!e.Handled && BubbleMouseEvent.HasFlag (DeviceEventType.KeyPress)) FocusParent?.onKeyPress (sender, e); } #endregion diff --git a/Samples/ShowCase/ShowCase.cs b/Samples/ShowCase/ShowCase.cs index 5aa36fa7..cb094e4a 100644 --- a/Samples/ShowCase/ShowCase.cs +++ b/Samples/ShowCase/ShowCase.cs @@ -108,9 +108,9 @@ namespace ShowCase } - public override bool OnKeyDown (Key key) { + public override bool OnKeyDown (KeyEventArgs e) { - switch (key) { + switch (e.Key) { case Key.F5: Load ("#ShowCase.DebugLog.crow").DataSource = this; return true; @@ -129,7 +129,7 @@ namespace ShowCase } return true; } - return base.OnKeyDown (key); + return base.OnKeyDown (e); } } }
\ No newline at end of file diff --git a/Samples/ShowCase/ui/showcase.crow b/Samples/ShowCase/ui/showcase.crow index de32059e..c64fa740 100644 --- a/Samples/ShowCase/ui/showcase.crow +++ b/Samples/ShowCase/ui/showcase.crow @@ -25,7 +25,7 @@ <ListItem ContextCommands="{GetCommands}" Selected="{/exp.Background=${ControlHighlight}}" Unselected="{/exp.Background=Transparent}"> - <Expandable Name="exp" Caption="{Name}" MouseDoubleClick="/onClickForExpand" > + <Expandable Name="exp" Caption="{Name}" MouseDoubleClick="/onClickForExpand" BubbleMouseEvent="MouseWheel|Keyboard|ButtonDown"> <Template> <VerticalStack> <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand" diff --git a/Samples/common/src/Editor.cs b/Samples/common/src/Editor.cs index c093d345..e56914f8 100644 --- a/Samples/common/src/Editor.cs +++ b/Samples/common/src/Editor.cs @@ -19,7 +19,7 @@ namespace Crow XmlSource source; object TokenMutex = new object(); - void parse () { + void parse () { XmlSource tmp = new XmlSource(_text); lock(TokenMutex) source = tmp; @@ -41,7 +41,7 @@ namespace Crow if (suggestions == null || suggestions.Count == 0) hideOverlay (); else - showOverlay (); + showOverlay (); } } bool suggestionsActive => overlay != null && overlay.IsVisible; @@ -58,9 +58,9 @@ namespace Crow m.GetCustomAttribute<XmlIgnoreAttribute>() == null); } MemberInfo getCrowTypeMember (string crowTypeName, string memberName) { - Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName); + Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName); return crowType.GetMember (memberName, BindingFlags.Public | BindingFlags.Instance).FirstOrDefault (); - } + } public override void OnTextChanged(object sender, TextChangeEventArgs e) { @@ -68,7 +68,7 @@ namespace Crow //Task.Run(()=>parse()); parse(); - + if (!disableSuggestions && HasFocus) tryGetSuggestions (); @@ -77,7 +77,7 @@ namespace Crow void tryGetSuggestions () { if (!currentLoc.HasValue) return; - int pos = lines.GetAbsolutePosition (CurrentLoc.Value); + int pos = lines.GetAbsolutePosition (CurrentLoc.Value); currentToken = source.FindTokenIncludingPosition (pos); currentNode = source.FindNodeIncludingPosition (pos); Console.WriteLine ($"Current Token: {currentToken} Current Node: {currentNode}"); @@ -109,7 +109,7 @@ namespace Crow else if (pi.PropertyType == typeof (Measure)) Suggestions = (new string[] {"Stretched", "Fit"}). Where (s => s.StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList (); - else if (pi.PropertyType == typeof (Fill)) + else if (pi.PropertyType == typeof (Fill)) Suggestions = EnumsNET.Enums.GetValues<Colors> () .Where (s => s.ToString().StartsWith (currentToken.AsString (_text), StringComparison.OrdinalIgnoreCase)).ToList (); } @@ -123,7 +123,7 @@ namespace Crow Suggestions = Enum.GetNames (pi.PropertyType).ToList (); else if (pi.PropertyType == typeof(bool)) Suggestions = new List<string> (new string[] {"true", "false"}); - else if (pi.PropertyType == typeof (Fill)) + else if (pi.PropertyType == typeof (Fill)) Suggestions = EnumsNET.Enums.GetValues<Colors> ().ToList (); else if (pi.PropertyType == typeof (Measure)) Suggestions = new List<string> (new string[] {"Stretched", "Fit"}); @@ -131,10 +131,10 @@ namespace Crow } } } - } - } else if (currentToken.Type != TokenType.AttributeValueClose && - currentToken.Type != TokenType.EmptyElementClosing && - currentToken.Type != TokenType.ClosingSign && + } + } else if (currentToken.Type != TokenType.AttributeValueClose && + currentToken.Type != TokenType.EmptyElementClosing && + currentToken.Type != TokenType.ClosingSign && currentNode is ElementStartTagSyntax eltStartTag) { if (currentToken.Type == TokenType.AttributeName) Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (_text)) @@ -157,39 +157,39 @@ namespace Crow overlay = IFace.LoadIMLFragment<ListBox>(@" <ListBox Style='suggestionsListBox' Data='{Suggestions}' UseLoadingThread='False' > <ItemTemplate> - <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left' + <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left' Selected = '{Background=${ControlHighlight}}' Unselected = '{Background=Transparent}'> <Label Text='{}' HorizontalAlignment='Left' /> - </ListItem> + </ListItem> </ItemTemplate> <ItemTemplate DataType='System.Reflection.MemberInfo'> - <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left' + <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left' Selected = '{Background=${ControlHighlight}}' Unselected = '{Background=Transparent}'> <HorizontalStack> <Image Picture='{GetIcon}' Width='16' Height='16'/> <Label Text='{Name}' HorizontalAlignment='Left' /> </HorizontalStack> - </ListItem> - </ItemTemplate> + </ListItem> + </ItemTemplate> <ItemTemplate DataType='Colors'> - <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left' + <ListItem Height='Fit' Margin='0' Focusable='false' HorizontalAlignment='Left' Selected = '{Background=${ControlHighlight}}' Unselected = '{Background=Transparent}'> <HorizontalStack> <Widget Background='{}' Width='20' Height='14'/> <Label Text='{}' HorizontalAlignment='Left' /> </HorizontalStack> - </ListItem> + </ListItem> </ItemTemplate> </ListBox> "); overlay.DataSource = this; - overlay.Loaded += (sender, arg) => (sender as ListBox).SelectedIndex = 0; + overlay.Loaded += (sender, arg) => (sender as ListBox).SelectedIndex = 0; } else overlay.IsVisible = true; - overlay.RegisterForLayouting(LayoutingType.Sizing); + overlay.RegisterForLayouting(LayoutingType.Sizing); } } void hideOverlay () { @@ -197,7 +197,7 @@ namespace Crow return; overlay.IsVisible = false; } - void completeToken () { + void completeToken () { string selectedSugg = overlay.SelectedItem is MemberInfo mi ? mi.Name : overlay.SelectedItem?.ToString (); if (selectedSugg == null) @@ -208,15 +208,15 @@ namespace Crow update (new TextChange (currentToken.End, 0, selectedSugg)); else if (currentToken.Type == TokenType.AttributeName && currentNode is AttributeSyntax attrib) { if (attrib.ValueToken.HasValue) { - TextChange tc = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + TextChange tc = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); update (tc); selectionStart = lines.GetLocation (attrib.ValueToken.Value.Start + tc.CharDiff + 1); CurrentLoc = lines.GetLocation (attrib.ValueToken.Value.End + tc.CharDiff - 1); } else { update (new TextChange (currentToken.Start, currentToken.Length, selectedSugg + "=\"\"")); MoveLeft (); - } - } else + } + } else update (new TextChange (currentToken.Start, currentToken.Length, selectedSugg)); hideOverlay (); } @@ -224,7 +224,7 @@ namespace Crow hideOverlay (); base.onMouseDown (sender, e); } - + public override void onKeyDown(object sender, KeyEventArgs e) { TextSpan selection = Selection; @@ -264,7 +264,7 @@ namespace Crow disableSuggestions = true; if (IFace.Shift) { - for (int l = lineStart; l <= lineEnd; l++) { + for (int l = lineStart; l <= lineEnd; l++) { if (Text[lines[l].Start] == '\t') update (new TextChange (lines[l].Start, 1, "")); else if (Char.IsWhiteSpace (Text[lines[l].Start])) { @@ -276,10 +276,10 @@ namespace Crow } }else{ - for (int l = lineStart; l <= lineEnd; l++) - update (new TextChange (lines[l].Start, 0, "\t")); + for (int l = lineStart; l <= lineEnd; l++) + update (new TextChange (lines[l].Start, 0, "\t")); } - + selectionStart = new CharLocation (lineStart, 0); CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length); @@ -288,7 +288,7 @@ namespace Crow return; } base.onKeyDown(sender, e); - } + } protected override void drawContent (Context gr) { try { @@ -297,7 +297,7 @@ namespace Crow base.drawContent (gr); return; } - + Rectangle cb = ClientRectangle; fe = gr.FontExtents; double lineHeight = fe.Ascent + fe.Descent; @@ -359,7 +359,7 @@ namespace Crow TextExtents extents; int tokPtr = 0; Token tok = source.Tokens[tokPtr]; - bool multilineToken = false; + bool multilineToken = false; ReadOnlySpan<char> buff = sourceBytes; @@ -369,7 +369,7 @@ namespace Crow if (multilineToken) { if (tok.End < lines[i].End) {//last incomplete line of multiline token - buff = sourceBytes.Slice (lines[i].Start, tok.End - lines[i].Start); + buff = sourceBytes.Slice (lines[i].Start, tok.End - lines[i].Start); } else {//print full line buff = sourceBytes.Slice (lines[i].Start, lines[i].Length); } @@ -399,7 +399,7 @@ namespace Crow gr.SetSource(Colors.DarkSlateBlue); }else { gr.SetSource(Colors.Red); - } + } } int size = buff.Length * 4 + 1; @@ -414,7 +414,7 @@ namespace Crow gr.MoveTo (pixX, lineHeight * y + fe.Ascent); gr.ShowText (bytes.Slice (0, encodedBytes)); pixX += extents.XAdvance; - x += buff.Length; + x += buff.Length; } if (multilineToken) { @@ -432,7 +432,7 @@ namespace Crow if (HasFocus && selectionNotEmpty) { RectangleD lineRect = new RectangleD (cb.X, lineHeight * y + cb.Top, pixX, lineHeight); RectangleD selRect = lineRect; - + if (i >= selStart.Line && i <= selEnd.Line) { if (selStart.Line == selEnd.Line) { selRect.X = selStart.VisualCharXPosition + cb.X; @@ -478,7 +478,7 @@ namespace Crow x = 0; pixX = 0; - + y++; @@ -492,13 +492,13 @@ namespace Crow continue; } else if (tok2.Type == TokenType.WhiteSpace) { x += tok2.Length; - pixX += spacePixelWidth * tok2.Length;*/ - } + pixX += spacePixelWidth * tok2.Length;*/ + } //gr.Translate (ScrollX, ScrollY); } } catch { - + } - } + } } }
\ No newline at end of file diff --git a/Samples/common/src/SampleBase.cs b/Samples/common/src/SampleBase.cs index 0622c2cf..b44bb8f5 100644 --- a/Samples/common/src/SampleBase.cs +++ b/Samples/common/src/SampleBase.cs @@ -70,11 +70,18 @@ namespace Samples new ActionCommand("File command three", (sender) => showMsgBox (sender)) ); public ActionCommand SingleCommand => new ActionCommand("Single command 1", (sender) => showMsgBox (sender), "#Icons.gavel.svg"); - public ToggleCommand CMDToggleBoolVal => new ToggleCommand ("Toggle", this, "BoolVal", "#Icons.gavel.svg", true); - public ToggleCommand CMDToggleBoolValField => new ToggleCommand ("ToggleField", this, "boolVal", "#Icons.gavel.svg", true); + public ToggleCommand CMDToggleBoolVal, CMDToggleBoolValField; + + public ActionCommand CMDHosted; void initCommands() { + CMDToggleBoolVal = new ToggleCommand (this, "Toggle", new Binding<bool> ("BoolVal"), "#Icons.gavel.svg", null, true); + CMDToggleBoolValField = new ToggleCommand (this, "ToggleField", new Binding<bool> ("boolVal"), "#Icons.gavel.svg", null, true); + CMDHosted = new ActionCommand (this, "Hosted command", + () => MessageBox.ShowModal(this, MessageBox.Type.Information, "hosted command triggered"), "#Icons.binding.svg", + new KeyBinding (Key.F1, Modifier.Super), + new Binding<bool> ("CanExecute")); Commands = new CommandGroup("commands msg boxes", new ActionCommand("Action 1", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 1 clicked")), new ActionCommand("Action two", () => MessageBox.ShowModal(this, MessageBox.Type.Information, "context menu 2 clicked"), null, false), @@ -339,7 +346,7 @@ namespace Samples string curSources = ""; - public bool boolVal = true; + public bool boolVal = true, canExecute; public string CurSources { get => curSources; @@ -358,7 +365,16 @@ namespace Samples return; boolVal = value; NotifyValueChanged(boolVal); - Console.WriteLine ($"boolVal => {value}"); + } + } + public bool CanExecute + { + get => canExecute; + set { + if (canExecute == value) + return; + canExecute = value; + NotifyValueChanged(canExecute); } } public Color AllWidgetBackground { @@ -387,10 +403,10 @@ namespace Samples base.processDrawing(ctx); } - public override bool OnKeyDown(Key key) + public override bool OnKeyDown(KeyEventArgs e) { - switch (key) + switch (e.Key) { case Key.F5: Load("Interfaces/Divers/testFileDialog.crow").DataSource = this; @@ -407,7 +423,7 @@ namespace Samples DbgLogger.Save(this); return true; } - return base.OnKeyDown(key); + return base.OnKeyDown(e); } } }
\ No newline at end of file diff --git a/Samples/common/src/SampleBaseForEditor.cs b/Samples/common/src/SampleBaseForEditor.cs index 47b93103..e1cadc0a 100644 --- a/Samples/common/src/SampleBaseForEditor.cs +++ b/Samples/common/src/SampleBaseForEditor.cs @@ -27,11 +27,11 @@ namespace Samples if (string.IsNullOrEmpty (CurrentDir)) CurrentDir = Path.Combine (Directory.GetCurrentDirectory (), "Interfaces"); - + } protected const string _defaultFileName = "unnamed.txt"; - protected string source = "", origSource; + protected string source = "", origSource; protected TextBox editor; bool debugLogRecording; @@ -53,7 +53,7 @@ namespace Samples Configuration.Global.Set (nameof (CurrentFile), value); NotifyValueChanged (CurrentFile); } - } + } public virtual string Source { get => source; set { @@ -71,8 +71,8 @@ namespace Samples get => Configuration.Global.Get<DbgEvtType> (nameof (RecordedEvents)); set { if (RecordedEvents == value) - return; - Configuration.Global.Set (nameof (RecordedEvents), value); + return; + Configuration.Global.Set (nameof (RecordedEvents), value); if (DebugLogRecording) DbgLogger.IncludeEvents = RecordedEvents; NotifyValueChanged(RecordedEvents); @@ -102,7 +102,7 @@ namespace Samples get => Configuration.Global.Get<bool> (nameof(DebugLogToFile)); set { if (DebugLogToFile == value) - return; + return; Configuration.Global.Set (nameof(DebugLogToFile), value); NotifyValueChanged(DebugLogToFile); DbgLogger.ConsoleOutput = !value; @@ -134,7 +134,7 @@ namespace Samples public CommandGroup EditorCommands => new CommandGroup (CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDSave, CMDSaveAs); protected virtual void initCommands () { - CMDNew = new ActionCommand ("New", new Action (onNewFile), "#Icons.blank-file.svg"); + CMDNew = new ActionCommand ("New", new Action (onNewFile), "#Icons.blank-file.svg"); CMDSave = new ActionCommand ("Save", new Action (onSave), "#Icons.save.svg", false); CMDSaveAs = new ActionCommand ("Save As...", new Action (onSaveAs), "#Icons.save.svg"); CMDQuit = new ActionCommand ("Quit", new Action (() => base.Quit ()), "#Icons.exit.svg"); @@ -149,7 +149,7 @@ namespace Samples protected Stack<TextChange> undoStack = new Stack<TextChange> (); protected Stack<TextChange> redoStack = new Stack<TextChange> (); protected TextSpan selection; - protected string SelectedText => + protected string SelectedText => selection.IsEmpty ? "" : Source.AsSpan (selection.Start, selection.Length).ToString (); protected void undo () { @@ -176,7 +176,7 @@ namespace Samples undoStack.Clear (); redoStack.Clear (); CMDUndo.CanExecute = false; - CMDRedo.CanExecute = false; + CMDRedo.CanExecute = false; } protected void cut () { @@ -186,11 +186,11 @@ namespace Samples protected void copy () { Clipboard = SelectedText; } - protected void paste () { + protected void paste () { applyChange (new TextChange (selection.Start, selection.Length, Clipboard)); } protected void onNewFile () { - if (IsDirty) { + if (IsDirty) { MessageBox.ShowModal (this, MessageBox.Type.YesNo, "Current file has unsaved changes, are you sure?").Yes += (sender, e) => newFile (); } else newFile (); @@ -257,7 +257,7 @@ namespace Samples CMDSave.CanExecute = false; } - protected void reloadFromFile () { + protected void reloadFromFile () { disableTextChangedEvent = true; if (File.Exists (CurrentFile)) { using (Stream s = new FileStream (CurrentFile, FileMode.Open)) { @@ -267,7 +267,7 @@ namespace Samples } disableTextChangedEvent = false; resetUndoRedo (); - } + } protected bool disableTextChangedEvent = false; protected void apply (TextChange change) { Span<char> tmp = stackalloc char[source.Length + (change.ChangedText.Length - change.Length)]; @@ -278,8 +278,8 @@ namespace Samples src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); disableTextChangedEvent = true; Source = tmp.ToString (); - disableTextChangedEvent = false; - } + disableTextChangedEvent = false; + } protected void applyChange (TextChange change) { undoStack.Push (change.Inverse (source)); redoStack.Clear (); @@ -319,8 +319,8 @@ namespace Samples return; applyChange (e.Change); } - - protected void onSelectedTextChanged (object sender, EventArgs e) { + + protected void onSelectedTextChanged (object sender, EventArgs e) { selection = (sender as Label).Selection; Console.WriteLine($"selection:{selection.Start} length:{selection.Length}"); CMDCut.CanExecute = CMDCopy.CanExecute = !selection.IsEmpty; diff --git a/Samples/common/ui/Interfaces/Experimental/hostedCommand.crow b/Samples/common/ui/Interfaces/Experimental/hostedCommand.crow new file mode 100644 index 00000000..444fbe21 --- /dev/null +++ b/Samples/common/ui/Interfaces/Experimental/hostedCommand.crow @@ -0,0 +1,13 @@ +<VerticalStack Fit="true"> + <CheckBox Caption="Can execute ?" IsChecked="{²CanExecute}"/> + <Button Command="{CMDHosted}" Foreground="White" Background="Jet"> + <Template> +<Border Style="ButtonBorder" Background="{./Background}" Name="Content" CornerRadius="{../CornerRadius}" > + <HorizontalStack DataSource="{./Command}"> + <Label Font="Arial, 14" Name="caption" Margin="${ButtonCaptionMargin}" Foreground="{./Foreground}" Text="{./Caption}"/> + <Label Font="Condensed, 9" Text="{KeyBinding}" Foreground="Grey"/> + </HorizontalStack> +</Border> + </Template> + </Button> +</VerticalStack>
\ No newline at end of file diff --git a/Samples/common/ui/Interfaces/Experimental/toggleCmd.crow b/Samples/common/ui/Interfaces/Experimental/toggleCmd.crow index 29dcf7e0..40a39abb 100644 --- a/Samples/common/ui/Interfaces/Experimental/toggleCmd.crow +++ b/Samples/common/ui/Interfaces/Experimental/toggleCmd.crow @@ -1,7 +1,7 @@ <VerticalStack Fit="true"> <HorizontalStack Fit="true"> <Button Command="{CMDToggleBoolVal}"/> - <CheckBox IsChecked="{²BoolVal}"/> + <CheckBox Caption="direct bound to bool value" IsChecked="{²BoolVal}" Background="Jet"/> <Button Command="{CMDToggleBoolVal}"> <Template> <Container DataSource="{./Command}"> |