]> O.S.I.I.S - jp/crow.git/commitdiff
ToggleCommand creation
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Tue, 14 Sep 2021 07:50:25 +0000 (07:50 +0000)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Tue, 14 Sep 2021 07:50:25 +0000 (07:50 +0000)
13 files changed:
Crow/src/Command/Command.cs
Crow/src/Command/CommandBase.cs
Crow/src/Command/CommandGroup.cs
Crow/src/Command/ToggleCommand.cs [new file with mode: 0644]
Crow/src/ExtensionsMethods.cs
Crow/src/Widgets/Button.cs
Crow/src/Widgets/Expandable.cs
Crow/src/Widgets/Menu.cs
Crow/src/Widgets/MenuItem.cs
Crow/src/Widgets/Widget.cs
Directory.Build.props
Samples/common/src/SampleBase.cs
Samples/common/ui/Interfaces/Experimental/toggleCmd.crow [new file with mode: 0644]

index 80bbf462ec873c8409aeaaaf1ebb6fc9a93d0584..6b8851e5c607b98912ae65564030cd83d39ee4c7 100644 (file)
@@ -6,15 +6,19 @@ using System;
 using System.ComponentModel;
 using System.Threading.Tasks;
 
-namespace Crow {       
+namespace Crow {
        /// <summary>
-       /// helper class to bind in one step icon, caption, action, and validity tests to a controls 
+       /// helper class to bind in one step icon, caption, action, and validity tests to a controls
        /// </summary>
        public class Command : CommandBase
        {
 
                #region CTOR
                public Command () {}
+               public Command (string caption, string icon = null, bool _canExecute = true)
+                       :base (caption, icon)
+               {}
+
                /// <summary>
                /// Initializes a new instance of Command with the action passed as argument.
                /// </summary>
@@ -31,23 +35,23 @@ namespace Crow {
                }
                public Command (string caption, Action executeAction, string icon = null, bool _canExecute = true)
                        :base (caption, icon)
-               {                                       
+               {
                        execute = executeAction;
                        canExecute = _canExecute;
                }
                public Command (string caption, Action<object> executeAction, string icon = null, bool _canExecute = true)
                        :base (caption, icon)
-               {                                       
+               {
                        execute = executeAction;
                        canExecute = _canExecute;
                }
-               
+
                #endregion
 
-               Delegate execute;               
+               Delegate execute;
 
                bool canExecute = true;
-               
+
                /// <summary>
                /// if true, action defined in this command may be executed,
                /// </summary>
@@ -60,7 +64,7 @@ namespace Crow {
                                canExecute = value;
                                NotifyValueChanged ("CanExecute", canExecute);
                        }
-               }               
+               }
 
                /// <summary>
                /// trigger the execution of the command
@@ -68,7 +72,7 @@ namespace Crow {
                public virtual void Execute (object sender = null){
                        if (execute != null && CanExecute){
                                Task task =     (execute is Action a) ?
-                                       task = new Task(a) :                            
+                                       task = new Task(a) :
                                (execute is Action<object> o) ?
                                        task = new Task(o, sender) : throw new Exception("Invalid Delegate type in Crow.Command, expecting Action or Action<object>");
                                task.Start();
index ed360333b85b44f461867eb58fedb4ad94a32d17..8a01eb14af7633bc8990f79f1d7203d4d2640343 100644 (file)
@@ -18,16 +18,16 @@ namespace Crow {
                        ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value));
                }
                #endregion
-               
+
                #region CTOR
                protected CommandBase() {}
                protected CommandBase (string _caption, string _icon = null)
                {
-                       caption = _caption;                     
+                       caption = _caption;
                        icon = _icon;
                }
                #endregion
-               
+
                string caption, icon;
 
                /// <summary>
@@ -46,7 +46,7 @@ namespace Crow {
                }
                /// <summary>
                /// Icon to display in the bound control
-               /// </summary>          
+               /// </summary>
                public string Icon {
                        get => icon;
                        set {
@@ -56,7 +56,7 @@ namespace Crow {
                                NotifyValueChanged ("Icon", icon);
                        }
                }
-               internal virtual void raiseAllValuesChanged() {         
+               internal virtual void raiseAllValuesChanged() {
                        NotifyValueChanged ("Icon", icon);
                        NotifyValueChanged ("Caption", caption);
                }
index 039f1a497eef7880f03570a36b2050a00aca5803..afe13e6c5cb23c9b952cce2d284b37365aad60b2 100644 (file)
@@ -4,6 +4,7 @@
 
 using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace Crow {
        public class CommandGroup : CommandBase, IEnumerable, IList<CommandBase>
@@ -25,7 +26,7 @@ namespace Crow {
                        Commands = new ObservableList<CommandBase>(commands);
                }
 
-               
+
                public int Count => Commands.Count;
 
                public bool IsReadOnly => false;
@@ -46,11 +47,24 @@ namespace Crow {
 
                public bool Contains(CommandBase item) => Commands.Contains (item);
 
-               public void CopyTo(CommandBase[] array, int arrayIndex) => Commands.CopyTo (array, arrayIndex);         
+               public void CopyTo(CommandBase[] array, int arrayIndex) => Commands.CopyTo (array, arrayIndex);
 
                public bool Remove(CommandBase item) => Commands.Remove (item);
 
                IEnumerator<CommandBase> IEnumerable<CommandBase>.GetEnumerator()
                        => Commands.GetEnumerator();
+               public void Add (params CommandBase[] items) {
+                       foreach  (CommandBase c in items)
+                               Commands.Add (c);
+               }
+               public void Remove (params CommandBase[] items) {
+                       foreach  (CommandBase c in items)
+                               Commands.Remove (c);
+               }
+               ///
+               public void ToggleAllCommand (bool canExecute) {
+                       foreach  (Command c in Commands.OfType<Command> ())
+                               c.CanExecute = canExecute;
+               }
        }
 }
diff --git a/Crow/src/Command/ToggleCommand.cs b/Crow/src/Command/ToggleCommand.cs
new file mode 100644 (file)
index 0000000..42a4476
--- /dev/null
@@ -0,0 +1,137 @@
+// Copyright (c) 2013-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.ComponentModel;
+using System.Reflection;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Reflection.Emit;
+
+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
+       {
+               object instance;
+               string memberName;
+               Action<bool> delSet;
+               Func<bool> delGet;
+
+               #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)
+               {
+                       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;
+
+                       CanExecute = _canExecute;
+               }
+               void instance_valueChanged (object sender, ValueChangeEventArgs e) {
+                       if (e.MemberName != memberName)
+                               return;
+                       Console.WriteLine ($"ToggleCommand valueChanged triggered => {e.NewValue}");
+
+                       bool tog = (bool)e.NewValue;
+                       NotifyValueChanged ("IsToggled", tog);
+                       if (tog)
+                               ToggleOn.Raise (this, null);
+                       else
+                               ToggleOff.Raise (this, null);
+
+               }
+               #endregion
+
+               /// <summary>
+               /// trigger the execution of the command
+               /// </summary>
+               public override void Execute (object sender = null){
+                       if (CanExecute)
+                               IsToggled = !IsToggled;
+               }
+
+               internal override void raiseAllValuesChanged()
+               {
+                       base.raiseAllValuesChanged();
+                       NotifyValueChanged ("IsToggled", IsToggled);
+               }
+
+               #region IToggle implementation
+
+               public event EventHandler ToggleOn;
+               public event EventHandler ToggleOff;
+               public BooleanTestOnInstance IsToggleable {get; set; }
+               public bool IsToggled {
+                       get => delGet ();
+                       set {
+                               if (value == IsToggled)
+                                       return;
+                               delSet (value);
+                               NotifyValueChanged ("IsToggled", IsToggled);
+                               Console.WriteLine ($"ToggleCommand.IsToggled => {value}");
+
+                               if (IsToggled)
+                                       ToggleOn.Raise (this, null);
+                               else
+                                       ToggleOff.Raise (this, null);
+                       }
+               }
+               #endregion
+
+               bool disposedValue;
+               protected virtual 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);
+               }
+       }
+}
index bc9268773fd853331eee36c513197d07350538e1..5bd9382ebb7bf06e05115cae5aa952c361d39695 100644 (file)
@@ -53,7 +53,7 @@ namespace Crow
                }
                public static void DrawCote(this Context ctx, PointD p1, PointD p2,
                        double stroke = 1.0, bool fill = false, double arrowWidth = 3.0, double arrowLength = 7.0)
-               {                       
+               {
                        PointD vDir = p2.Substract(p1);
                        vDir = vDir.GetNormalized ();
                        PointD vPerp = vDir.GetPerp ();
@@ -69,7 +69,7 @@ namespace Crow
                                ctx.LineTo (pA0.Substract (vA));
                        else
                                ctx.MoveTo (pA0.Substract (vA));
-                       
+
                        ctx.LineTo (p1);
 
                        ctx.MoveTo (p2);
@@ -90,7 +90,7 @@ namespace Crow
                }
                public static void DrawCoteInverse(this Context ctx, PointD p1, PointD p2,
                        double stroke = 1.0, bool fill = false, double arrowWidth = 3.0, double arrowLength = 7.0)
-               {                       
+               {
                        PointD vDir = p2.Substract(p1);
                        vDir = vDir.GetNormalized ();
                        PointD vPerp = vDir.GetPerp ();
@@ -122,7 +122,7 @@ namespace Crow
                }
                public static void DrawCoteFixed(this Context ctx, PointD p1, PointD p2,
                        double stroke = 1.0, double coteWidth = 3.0)
-               {                       
+               {
                        PointD vDir = p2.Substract(p1);
                        vDir = vDir.GetNormalized ();
                        PointD vPerp = vDir.GetPerp ();
@@ -164,7 +164,7 @@ namespace Crow
                        case Alignment.BottomLeft:
                                return Alignment.TopRight;
                        case Alignment.BottomRight:
-                               return Alignment.TopLeft;                       
+                               return Alignment.TopLeft;
                        }
                        return Alignment.Center;
                }
@@ -187,11 +187,11 @@ namespace Crow
                        return c == '\t' || c.IsAnyLineBreakCharacter() || char.IsWhiteSpace (c);
                }
                public static object GetDefaultValue(this object obj)
-               {                       
+               {
                        Type t = obj.GetType ();
                        if (t.IsValueType)
                                return Activator.CreateInstance (t);
-                       
+
                        return null;
                }
 
@@ -203,7 +203,7 @@ namespace Crow
                        }
                }
 
-               internal static bool IsAnyLineBreakCharacter (this char c) 
+               internal static bool IsAnyLineBreakCharacter (this char c)
                        => c == '\n' || c == '\r' || c == '\u0085' || c == '\u2028' || c == '\u2029';
 
                public static bool TryGetResource (this Assembly a, string resId, out Stream stream) {
index 009aed78be37ebc76ccad76ac81f5d0125e5e977..6c0056295c52280c17fd7c38ae357bbdbeb34280 100644 (file)
@@ -119,6 +119,11 @@ namespace Crow
                        }
                        NotifyValueChanged (mName, e.NewValue);
                }
-
+               protected override void Dispose(bool disposing)
+               {
+                       if (command is IDisposable dis)
+                               dis.Dispose ();
+                       base.Dispose(disposing);
+               }
        }
 }
index 1e334c2e77f6687b18f1deca54642d21e7895457..95e743ea8818e2e0bbece9c157be36394eece234 100644 (file)
@@ -85,7 +85,7 @@ namespace Crow
                                bool isExp = IsExpandable;
                                NotifyValueChanged ("IsExpandable", isExp);
                                if (!isExp)
-                                       _isExpanded = false;                            
+                                       _isExpanded = false;
 
                                NotifyValueChangedAuto (_isExpanded);
                                NotifyValueChanged ("IsToggled",_isExpanded);
index 1abf7dd4146606af7e034fa41e7671f910422fad..6b446bbea04706063474f1421893ff91204377a1 100644 (file)
@@ -39,7 +39,7 @@ namespace Crow {
                #endregion
 
                public override void AddItem (Widget g)
-               {                       
+               {
                        base.AddItem (g);
 
                        if (orientation == Orientation.Horizontal)
index b00101a56ae30647bc4d05dd39f51b1aed126b6b..c861441f27cdb4cfe5b5da8ccb87111f1a51c084 100644 (file)
@@ -62,17 +62,17 @@ namespace Crow
                                NotifyValueChangedAuto (command);
                        }
                }
-               
+
                public override bool IsEnabled {
                        get => Command == null ? base.IsEnabled : Command.CanExecute;
                        set => base.IsEnabled = value;
                }
-               
+
                public override string Caption {
                        get => Command == null ? base.Caption : Command.Caption;
                        set => base.Caption = value;
                }
-               
+
                public string Icon {
                        get => Command == null ? icon : Command.Icon;
                        set {
@@ -121,12 +121,12 @@ namespace Crow
                protected virtual void onOpen (object sender, EventArgs e){
                        Open.Raise (this, null);
                }
-               protected virtual void onClose (object sender, EventArgs e){                    
+               protected virtual void onClose (object sender, EventArgs e){
                        Close.Raise (this, null);
                }
                public override bool MouseIsIn (Point m)
                        => IsEnabled && !IsDragged ? base.MouseIsIn (m) || child.MouseIsIn (m) : false;
-               
+
                public override void onMouseEnter (object sender, MouseMoveEventArgs e)
                {
                        base.onMouseEnter (sender, e);
@@ -151,10 +151,10 @@ namespace Crow
                        if (hasClick)
                                base.onMouseClick (sender, e);
 
-                       if (!IsOpened) 
+                       if (!IsOpened)
                                if (LogicalParent is Menu m)
                                        m.AutomaticOpening = false;
-                       
+
                }
                void closeMenu () {
                        MenuItem tmp = LogicalParent as MenuItem;
@@ -165,6 +165,12 @@ namespace Crow
                                tmp = tmp.LogicalParent as MenuItem;
                        }
                }
+               protected override void Dispose(bool disposing)
+               {
+                       if (command is IDisposable dis)
+                               dis.Dispose ();
+                       base.Dispose(disposing);
+               }
        }
 }
 
index 512d27e0f4a70a387034f251b14bcbbd4dc9eea2..4dbcb34070993f2b4721310ab23d274e212ff34c 100644 (file)
@@ -2334,7 +2334,7 @@ namespace Crow
                        LastSlots = default;
                        LastPaintedSlot = default;*/
                        //Slot = LastSlots = LastPaintedSlot = default;
-                       Slot = LastPaintedSlot = default;
+                       //Slot = LastPaintedSlot = default;
                }
        }
 }
index fe1f22e3a048394445e6e1859ddbd34988d3db13..a0ea5fb5d3b83658d81f7fe41b16c1e93a79cc92 100644 (file)
@@ -6,7 +6,7 @@
                <Authors>Jean-Philippe Bruyère</Authors>
                <LangVersion>7.3</LangVersion>
 
-               <CrowVersion>0.9.6</CrowVersion>
+               <CrowVersion>0.9.7</CrowVersion>
                <CrowPackageVersion>$(CrowVersion)-beta</CrowPackageVersion>
 
                <!-- If you dont have a native libstb on your system, enable the managed version of stb here
index fa6d1cde697da40320195d608da02e4997cf2030..1c516bfa63f94d061c4e5bee02f2aeb01ac10d04 100644 (file)
@@ -70,6 +70,8 @@ namespace Samples
                        new Command("File command three", (sender) => showMsgBox (sender))
                );
                public Command SingleCommand => new Command("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);
 
                void initCommands()
                {
@@ -291,7 +293,7 @@ namespace Samples
 
                                NotifyValueChanged(testString);
                        }
-               }               
+               }
                string prop1;
                public string TestList3SelProp1
                {
@@ -337,7 +339,7 @@ namespace Samples
 
 
                string curSources = "";
-               bool boolVal = true;
+               public bool boolVal = true;
                public string CurSources
                {
                        get => curSources;
@@ -356,6 +358,7 @@ namespace Samples
                                        return;
                                boolVal = value;
                                NotifyValueChanged(boolVal);
+                               Console.WriteLine ($"boolVal => {value}");
                        }
                }
                public Color AllWidgetBackground {
diff --git a/Samples/common/ui/Interfaces/Experimental/toggleCmd.crow b/Samples/common/ui/Interfaces/Experimental/toggleCmd.crow
new file mode 100644 (file)
index 0000000..29dcf7e
--- /dev/null
@@ -0,0 +1,55 @@
+<VerticalStack Fit="true">
+       <HorizontalStack Fit="true">
+               <Button Command="{CMDToggleBoolVal}"/>
+               <CheckBox IsChecked="{²BoolVal}"/>
+               <Button Command="{CMDToggleBoolVal}">
+                       <Template>
+                               <Container DataSource="{./Command}">
+                                       <CheckBox Caption="{./Caption}" Style="CheckBox2" IsChecked="{²IsToggled}"/>
+                               </Container>
+                       </Template>
+               </Button>
+               <Button Command="{CMDToggleBoolVal}">
+                       <Template>
+                               <Border DataSource="{./Command}" Name="Content" Margin="2"
+                                                       Background="DarkGrey" Tooltip="{./Caption}"
+                                                       Foreground="Transparent" CornerRadius="{../CornerRadius}" BorderWidth="1"
+                                                       MouseEnter="{Foreground=vgradient|0:White|0.2:Grey|0.9:Grey|1:Black}"
+                                                       MouseLeave="{Foreground=Transparent}"
+                                                       MouseDown="{Foreground=vgradient|0:Black|0.05:Grey|0.85:Grey|1:White};{Background=Yellow}"
+                                                       MouseUp="{Foreground=vgradient|0:White|0.2:Grey|0.9:Grey|1:Black};{Background=DarkGrey}">
+                                       <CheckBox Focusable="False" Width="Stretched" Height="Stretched" IsChecked="{²IsToggled}"
+                                                       Checked="{Background=${ControlHighlight}}"
+                                                       Unchecked="{Background=Jet}">
+                                               <Template>
+                                                       <Image Style="icon" Name="caption" Path="{Icon}" Background="{./Background}" Margin="10"/>
+                                               </Template>
+                                       </CheckBox>                                     
+                               </Border>
+                       </Template>
+               </Button>
+               <Button Command="{CMDToggleBoolVal}">
+                       <Template>
+                               <Container DataSource="{./Command}">
+<!--                                   <CheckBox Caption="{./Caption}" Style="CheckBox2" IsChecked="{²IsToggled}"/>-->
+                                       <CheckBox Width="Stretched" Height="Stretched" IsChecked="{²IsToggled}"
+                                                       Checked="{Background=${ControlHighlight}}"
+                                                       Unchecked="{Background=Jet}">
+                                               <Template>
+                                                       <Image Style="icon" Name="caption" Path="{Icon}" Background="{./Background}" Margin="10"/>
+                                               </Template>
+                                       </CheckBox>
+                               </Container>
+                       </Template>
+               </Button>
+       </HorizontalStack>
+       <HorizontalStack Fit="true">
+               <Button Command="{CMDToggleBoolValField}">
+                       <Template>
+                               <Container DataSource="{./Command}">
+                                       <CheckBox Caption="{./Caption}" Style="CheckBox2" IsChecked="{²IsToggled}"/>
+                               </Container>
+                       </Template>
+               </Button>
+       </HorizontalStack>
+</VerticalStack>
\ No newline at end of file