diff --git a/Source/Eto.Mac/Forms/Controls/StepperHandler.cs b/Source/Eto.Mac/Forms/Controls/StepperHandler.cs index 23b73d072a..b4b67aa49c 100644 --- a/Source/Eto.Mac/Forms/Controls/StepperHandler.cs +++ b/Source/Eto.Mac/Forms/Controls/StepperHandler.cs @@ -32,6 +32,21 @@ namespace Eto.Mac.Forms.Controls { + public class MacViewHandler2 : WidgetHandler2, IMacControlHandler2 + where TWidget : Control + where TControl : NSView, new() + { + public virtual NSView GetContainerControl(Widget widget) + { + return GetControl((TWidget)widget); + } + + public SizeF GetPreferredSize(Widget widget, SizeF availableSize) + { + return SizeF.Empty; + } + } + public class StepperHandler : MacControl, Stepper.IHandler { public class EtoStepper : NSStepper, IMacControl @@ -49,17 +64,9 @@ public StepperHandler() }; } - static object ValidDirection_Key = new object(); - - public StepperValidDirections ValidDirection - { - get { return Widget.Properties.Get(ValidDirection_Key, StepperValidDirections.Both); } - set { Widget.Properties.Set(ValidDirection_Key, value, UpdateState, StepperValidDirections.Both); } - } - void UpdateState() { - switch (ValidDirection) + switch (Widget.ValidDirection) { case StepperValidDirections.Both: Control.ValueWraps = true; @@ -94,12 +101,12 @@ public override bool Enabled void SetStepperEnabled() { - Control.Enabled = Enabled && ValidDirection != StepperValidDirections.None; + Control.Enabled = Enabled && Widget.ValidDirection != StepperValidDirections.None; } StepperDirection? GetDirection() { - switch (ValidDirection) + switch (Widget.ValidDirection) { case StepperValidDirections.Both: var dir = Control.IntValue == 1 ? StepperDirection.Up : StepperDirection.Down; @@ -123,22 +130,42 @@ void SetStepperEnabled() return null; } - public override void AttachEvent(string id) + public override void Initialize(Stepper widget) { - switch (id) + base.Initialize(widget); + GetControl(widget).SizeToFit(); + } + + public override Action SetProperty(object property) + { + if (property == Stepper.ValidDirectionProperty) + return SetValidDirection; + + return base.SetProperty(property); + } + + static void SetValidDirection(Stepper c, object value) + { + (c.Handler as StepperHandler)?.UpdateState(); + } + + public override Action GetEvent(object evt) + { + if (evt == Stepper.StepEvent) + return AttachStepEvent; + + return base.GetEvent(evt); + } + + static void AttachStepEvent(Stepper c) + { + var h = c.Handler as StepperHandler; + h.Control.Activated += (sender, e) => { - case Stepper.StepEvent: - Control.Activated += (sender, e) => - { - var dir = GetDirection(); - if (dir != null) - Callback.OnStep(Widget, new StepperEventArgs(dir.Value)); - }; - break; - default: - base.AttachEvent(id); - break; - } + var dir = h.GetDirection(); + if (dir != null) + Stepper.StepEvent.Raise(c, new StepperEventArgs(dir.Value)); + }; } } -} +} \ No newline at end of file diff --git a/Source/Eto.Mac/Forms/MacBase.cs b/Source/Eto.Mac/Forms/MacBase.cs index c126d52a63..6bd8a21148 100644 --- a/Source/Eto.Mac/Forms/MacBase.cs +++ b/Source/Eto.Mac/Forms/MacBase.cs @@ -23,6 +23,13 @@ namespace Eto.Mac.Forms { + public interface IMacControlHandler2 + { + NSView GetContainerControl(Widget widget); + + SizeF GetPreferredSize(Widget widget, SizeF availableSize); + } + public interface IMacControlHandler { NSView ContainerControl { get; } diff --git a/Source/Eto.Mac/Forms/MacControlExtensions.cs b/Source/Eto.Mac/Forms/MacControlExtensions.cs index 836dd8700e..b34f1374d3 100644 --- a/Source/Eto.Mac/Forms/MacControlExtensions.cs +++ b/Source/Eto.Mac/Forms/MacControlExtensions.cs @@ -55,6 +55,9 @@ public static SizeF GetPreferredSize(this Control control, SizeF availableSize) { return mh.GetPreferredSize(availableSize); } + var mh2 = control.GetMacControl2(); + if (mh2 != null) + return mh2.GetPreferredSize(control, availableSize); var c = control.ControlObject as NSControl; if (c != null) @@ -89,6 +92,17 @@ public static IMacControlHandler GetMacControl(this Control control) return child == null ? null : child.GetMacControl(); } + public static IMacControlHandler2 GetMacControl2(this Control control) + { + if (control == null) + return null; + var container = control.Handler as IMacControlHandler2; + if (container != null) + return container; + var child = control.ControlObject as Control; + return child == null ? null : child.GetMacControl2(); + } + public static NSView GetContainerView(this Widget control) { if (control == null) @@ -96,6 +110,9 @@ public static NSView GetContainerView(this Widget control) var containerHandler = control.Handler as IMacControlHandler; if (containerHandler != null) return containerHandler.ContainerControl; + var containerHandler2 = control.Handler as IMacControlHandler2; + if (containerHandler2 != null) + return containerHandler2.GetContainerControl(control); var childControl = control.ControlObject as Control; if (childControl != null) return childControl.GetContainerView(); diff --git a/Source/Eto.Mac/Platform.cs b/Source/Eto.Mac/Platform.cs index c50db02960..c05dc638c7 100644 --- a/Source/Eto.Mac/Platform.cs +++ b/Source/Eto.Mac/Platform.cs @@ -192,6 +192,8 @@ public static void AddTo(Eto.Platform p) // General p.Add(() => new EtoEnvironmentHandler()); p.Add(() => new ThreadHandler()); + + p.AddHandler(() => new StepperHandler()); } public override IDisposable ThreadStart() diff --git a/Source/Eto.Test/Eto.Test/Sections/Controls/StepperSection.cs b/Source/Eto.Test/Eto.Test/Sections/Controls/StepperSection.cs index 2723056589..64a8f79f97 100644 --- a/Source/Eto.Test/Eto.Test/Sections/Controls/StepperSection.cs +++ b/Source/Eto.Test/Eto.Test/Sections/Controls/StepperSection.cs @@ -1,5 +1,6 @@ using System; using Eto.Forms; + namespace Eto.Test.Sections.Controls { [Section("Controls", typeof(Stepper))] diff --git a/Source/Eto.WinRT/Eto.WinRT.csproj b/Source/Eto.WinRT/Eto.WinRT.csproj index 2825e6ef89..34efbeca73 100644 --- a/Source/Eto.WinRT/Eto.WinRT.csproj +++ b/Source/Eto.WinRT/Eto.WinRT.csproj @@ -17,6 +17,7 @@ 8.1 12 + ..\..\BuildOutput\obj\Eto.WinRT true diff --git a/Source/Eto/Eto - pcl.csproj b/Source/Eto/Eto - pcl.csproj index 000749216c..6e7de29e8d 100644 --- a/Source/Eto/Eto - pcl.csproj +++ b/Source/Eto/Eto - pcl.csproj @@ -344,6 +344,7 @@ + diff --git a/Source/Eto/EventLookup.cs b/Source/Eto/EventLookup.cs index a6bcf45cc4..3d14fbbe75 100644 --- a/Source/Eto/EventLookup.cs +++ b/Source/Eto/EventLookup.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Linq; using System.Linq.Expressions; +using Eto.Forms; namespace Eto { @@ -10,20 +11,27 @@ static class EventLookup { static readonly Dictionary> registeredEvents = new Dictionary>(); static readonly Assembly etoAssembly = typeof(EventLookup).GetAssembly(); - static readonly Dictionary externalEvents = new Dictionary(); + static readonly Dictionary externalEvents = new Dictionary(); struct EventDeclaration { - public readonly string Identifier; + public readonly object Identifier; public readonly MethodInfo Method; - public EventDeclaration(MethodInfo method, string identifier) + public EventDeclaration(MethodInfo method, object identifier) { Method = method; Identifier = identifier; } } + public static void Register(Expression> expression, object identifier) + { + var method = ((MethodCallExpression)expression.Body).Method; + var declarations = GetDeclarations(typeof(T)); + declarations.Add(new EventDeclaration(method, identifier)); + } + public static void Register(Expression> expression, string identifier) { var method = ((MethodCallExpression)expression.Body).Method; @@ -38,14 +46,18 @@ public static void HookupEvents(Widget widget) if (type.GetAssembly() == etoAssembly) return; + var handler2 = widget.Handler as IHandler2; var handler = widget.Handler as Widget.IHandler; - if (handler != null) + + var events = GetEvents(type); + for (int i = 0; i < events.Length; i++) { - var ids = GetEvents(type); - for (int i = 0; i < ids.Length; i++) - { - handler.HandleEvent(ids[i], true); - } + var evt = events[i]; + var id = evt as string; + if (id != null) + handler?.HandleEvent(id, true); + else + handler2?.AttachEvent(widget, evt); } } @@ -59,9 +71,9 @@ public static bool IsDefault(Widget widget, string identifier) return Array.IndexOf(events, identifier) >= 0; } - static string[] GetEvents(Type type) + static object[] GetEvents(Type type) { - string[] events; + object[] events; if (!externalEvents.TryGetValue(type, out events)) { events = FindTypeEvents(type).Distinct().ToArray(); @@ -70,7 +82,7 @@ static string[] GetEvents(Type type) return events; } - static IEnumerable FindTypeEvents(Type type) + static IEnumerable FindTypeEvents(Type type) { var externalTypes = new List(); var current = type; diff --git a/Source/Eto/Forms/Controls/Control.cs b/Source/Eto/Forms/Controls/Control.cs index 71a921519b..575213af99 100644 --- a/Source/Eto/Forms/Controls/Control.cs +++ b/Source/Eto/Forms/Controls/Control.cs @@ -21,7 +21,7 @@ namespace Eto.Forms [TypeConverter(typeof(ControlConverter))] public partial class Control : BindableWidget, IMouseInputSource, IKeyboardInputSource, ICallbackSource { - new IHandler Handler { get { return (IHandler)base.Handler; } } + new IHandler Handler { get { return base.Handler as IHandler; } } /// /// Gets a value indicating that the control is loaded onto a form, that is it has been created, added to a parent, and shown @@ -437,7 +437,7 @@ public event EventHandler PreLoad protected virtual void OnPreLoad(EventArgs e) { Properties.TriggerEvent(PreLoadKey, this, e); - Handler.OnPreLoad(e); + Handler?.OnPreLoad(e); } static readonly object LoadKey = new object(); @@ -470,7 +470,7 @@ protected virtual void OnLoad(EventArgs e) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Control was loaded more than once")); #endif Properties.TriggerEvent(LoadKey, this, e); - Handler.OnLoad(e); + Handler?.OnLoad(e); Loaded = true; } @@ -495,7 +495,7 @@ public event EventHandler LoadComplete protected virtual void OnLoadComplete(EventArgs e) { Properties.TriggerEvent(LoadCompleteKey, this, e); - Handler.OnLoadComplete(e); + Handler?.OnLoadComplete(e); } static readonly object UnLoadKey = new object(); @@ -524,7 +524,7 @@ protected virtual void OnUnLoad(EventArgs e) #endif Loaded = false; Properties.TriggerEvent(UnLoadKey, this, e); - Handler.OnUnLoad(e); + Handler?.OnUnLoad(e); } #endregion @@ -571,7 +571,7 @@ public Control(IHandler handler) /// public void Invalidate() { - Handler.Invalidate(true); + Handler?.Invalidate(true); } /// @@ -595,7 +595,7 @@ public void Invalidate(bool invalidateChildren) /// Rectangle to repaint public void Invalidate(Rectangle rect) { - Handler.Invalidate(rect, true); + Handler?.Invalidate(rect, true); } /// @@ -608,7 +608,7 @@ public void Invalidate(Rectangle rect) /// True to invalidate all children, false to only invalidate the container public void Invalidate(Rectangle rect, bool invalidateChildren) { - Handler.Invalidate(rect, invalidateChildren); + Handler?.Invalidate(rect, invalidateChildren); } /// @@ -625,16 +625,18 @@ public void Invalidate(Rectangle rect, bool invalidateChildren) /// The current size of the control public virtual Size Size { - get { return Handler.Size; } - set { Handler.Size = value; } + get { return Handler?.Size ?? Properties.Get(SizeProperty); } + set { if (Handler != null) Handler.Size = value; else Properties.Set(SizeProperty, value);} } + public static DependencyProperty SizeProperty = new DependencyProperty(); + /// /// Gets or sets the width of the control size. /// public virtual int Width { - get { return Handler.Size.Width; } + get { return Size.Width; } set { Size = new Size(value, Size.Height); } } @@ -643,7 +645,7 @@ public virtual int Width /// public virtual int Height { - get { return Handler.Size.Height; } + get { return Size.Height; } set { Size = new Size(Size.Width, value); } } @@ -679,17 +681,22 @@ protected virtual void OnEnabledChanged(EventArgs e) [DefaultValue(true)] public virtual bool Enabled { - get { return Handler.Enabled; } + get { return Handler?.Enabled ?? Properties.Get(EnabledProperty); } set { if (value != Enabled) { - Handler.Enabled = value; + if (Handler != null) + Handler.Enabled = value; + else + Properties.Set(EnabledProperty, value); OnEnabledChanged(EventArgs.Empty); } } } + public static readonly DependencyProperty EnabledProperty = new DependencyProperty(); + /// /// Gets or sets a value indicating whether this is visible to the user. /// @@ -702,10 +709,12 @@ public virtual bool Enabled [DefaultValue(true)] public virtual bool Visible { - get { return Handler.Visible; } - set { Handler.Visible = value; } + get { return Handler?.Visible ?? Properties.Get(VisibleProperty); } + set { if (Handler != null) Handler.Visible = value; else Properties.Set(VisibleProperty, value); } } + public static readonly DependencyProperty VisibleProperty = new DependencyProperty(); + /// /// Gets the container which this control has been added to, if any /// @@ -725,6 +734,7 @@ internal Container LogicalParent set { base.Parent = value; } } + public static readonly DependencyProperty VisualParentProperty = new DependencyProperty(); static readonly object VisualParent_Key = new object(); /// @@ -742,7 +752,7 @@ public Container VisualParent internal set { Properties.Set(VisualParent_Key, value); - Handler.SetParent(value); + Handler?.SetParent(value); } } @@ -863,7 +873,7 @@ public virtual bool HasFocus /// public virtual void Focus() { - Handler.Focus(); + Handler?.Focus(); } @@ -894,7 +904,7 @@ int SuspendCount public virtual void SuspendLayout() { SuspendCount++; - Handler.SuspendLayout(); + Handler?.SuspendLayout(); } /// @@ -911,7 +921,7 @@ public virtual void ResumeLayout() throw new InvalidOperationException("Control is not suspended. You must balance calls to Resume() with Suspend()"); SuspendCount = --count; - Handler.ResumeLayout(); + Handler?.ResumeLayout(); } /// @@ -975,7 +985,7 @@ public IEnumerable SupportedPlatformCommands /// public void MapPlatformCommand(string systemCommand, Command command) { - Handler.MapPlatformCommand(systemCommand, command); + Handler?.MapPlatformCommand(systemCommand, command); } /// @@ -985,7 +995,7 @@ public void MapPlatformCommand(string systemCommand, Command command) /// Point in screen space public PointF PointFromScreen(PointF point) { - return Handler.PointFromScreen(point); + return Handler?.PointFromScreen(point) ?? PointF.Empty; } /// @@ -995,7 +1005,7 @@ public PointF PointFromScreen(PointF point) /// Point in control space public PointF PointToScreen(PointF point) { - return Handler.PointToScreen(point); + return Handler?.PointToScreen(point) ?? PointF.Empty; } /// diff --git a/Source/Eto/Forms/Controls/Stepper.cs b/Source/Eto/Forms/Controls/Stepper.cs index 7f30cc52d6..4a1ef43343 100644 --- a/Source/Eto/Forms/Controls/Stepper.cs +++ b/Source/Eto/Forms/Controls/Stepper.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; - +using System.Collections.Generic; +using System.Linq.Expressions; namespace Eto.Forms { @@ -68,25 +69,13 @@ public StepperEventArgs(StepperDirection direction) } } - /// - /// Control that allows you to "step" through values, usually presented by two buttons arranged vertically with up and down arrows. - /// - [Handler(typeof(IHandler))] public class Stepper : Control { - new IHandler Handler { get { return (IHandler)base.Handler; } } - - /// - /// Identifier for the event. - /// - public const string StepEvent = "Stepper.Step"; + public static DependencyEvent StepEvent = new DependencyEvent((c, e) => c.OnStep(e)); - /// - /// Event to handle when the user clicks on one of the step buttons, either up or down. - /// public event EventHandler Step { - add { Properties.AddHandlerEvent(StepEvent, value); } + add { Properties.AddEvent(StepEvent, value); } remove { Properties.RemoveEvent(StepEvent, value); } } @@ -94,10 +83,10 @@ public event EventHandler Step /// Triggers the event. /// /// Event arguments - protected void OnStep(StepperEventArgs e) - { - Properties.TriggerEvent(StepEvent, this, e); - } + protected void OnStep(StepperEventArgs e) => Properties.TriggerEvent(StepEvent, this, e); + + + public static DependencyProperty ValidDirectionProperty = new DependencyProperty(StepperValidDirections.Both); /// /// Gets or sets the valid directions the stepper will allow the user to click. @@ -110,61 +99,8 @@ protected void OnStep(StepperEventArgs e) [DefaultValue(StepperValidDirections.Both)] public StepperValidDirections ValidDirection { - get { return Handler.ValidDirection; } - set { Handler.ValidDirection = value; } - } - - ICallback callback = new Callback(); - - /// - /// Gets the callback. - /// - /// The callback. - protected override object GetCallback() => callback; - - /// - /// Callback interface for the Stepper - /// - public new interface ICallback : Control.ICallback - { - /// - /// Triggers the event. - /// - /// Widget instance to trigger the event - /// Event arguments - void OnStep(Stepper widget, StepperEventArgs e); - } - - /// - /// Callback implementation for the Stepper - /// - protected new class Callback : Control.Callback, ICallback - { - /// - /// Triggers the event. - /// - /// Widget instance to trigger the event - /// Event arguments - public void OnStep(Stepper widget, StepperEventArgs e) - { - widget.Platform.Invoke(() => widget.OnStep(e)); - } - } - - /// - /// Handler interface for the Stepper - /// - public new interface IHandler : Control.IHandler - { - /// - /// Gets or sets the valid directions the stepper will allow the user to click. - /// - /// - /// On some platforms, the up and/or down buttons will not appear disabled, but will not trigger any events when they are - /// not set as a valid direction. - /// - /// The valid directions for the stepper. - StepperValidDirections ValidDirection { get; set; } + get { return Properties.Get(ValidDirectionProperty); } + set { Properties.Set(ValidDirectionProperty, value); } } } } diff --git a/Source/Eto/Forms/ThemedControls/ThemedStepperHandler.cs b/Source/Eto/Forms/ThemedControls/ThemedStepperHandler.cs index 2d1fdd224e..08f5b51903 100644 --- a/Source/Eto/Forms/ThemedControls/ThemedStepperHandler.cs +++ b/Source/Eto/Forms/ThemedControls/ThemedStepperHandler.cs @@ -135,14 +135,28 @@ public override void AttachEvent(string id) { switch (id) { - case Stepper.StepEvent: - upButton.Click += (sender, e) => Callback.OnStep(Widget, new StepperEventArgs(StepperDirection.Up)); - downButton.Click += (sender, e) => Callback.OnStep(Widget, new StepperEventArgs(StepperDirection.Down)); - break; + /*case Stepper.StepEvent: + upButton.Click += (sender, e) => Stepper.StepEvent.Callback(Widget, new StepperEventArgs(StepperDirection.Up)); + downButton.Click += (sender, e) => Stepper.StepEvent.Callback(Widget, new StepperEventArgs(StepperDirection.Down)); + break;*/ default: base.AttachEvent(id); break; } } + + public override Action GetEvent(object evt) + { + if (evt == Stepper.StepEvent) + return StepEventHandler; + return base.GetEvent(evt); + } + + static void StepEventHandler(Stepper c) + { + var h = c.Handler as ThemedStepperHandler; + h.upButton.Click += (sender, e) => Stepper.StepEvent.Raise(c, new StepperEventArgs(StepperDirection.Up)); + h.downButton.Click += (sender, e) => Stepper.StepEvent.Raise(c, new StepperEventArgs(StepperDirection.Down)); + } } } diff --git a/Source/Eto/Handler2.cs b/Source/Eto/Handler2.cs new file mode 100644 index 0000000000..ff4f493bbe --- /dev/null +++ b/Source/Eto/Handler2.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Eto +{ + public class DependencyProperty + { + internal object EventKey = new object(); + + public TValue DefaultValue { get; set; } + + public DependencyProperty() + { + } + + public DependencyProperty(TValue defaultValue) + { + DefaultValue = defaultValue; + } + + public bool IsSupported + { + get + { + var h = Platform.Instance.CreateShared(typeof(TControl)) as IHandler2; + return h.SupportsProperty(this); + } + } + } + + public class DependencyEvent + where TArgs : EventArgs + { + Action _callback; + Expression> _callbackExpression; + Action Callback => _callback ?? (_callback = _callbackExpression.Compile()); + + string _id; + public string ID => _id ?? (_id = Guid.NewGuid().ToString()); + + public DependencyEvent(Expression> callback) + { + _callbackExpression = callback; + EventLookup.Register(callback, this); + } + + public void Raise(TControl widget, TArgs e) + { + var callback = Callback; + if (callback != null) + { + using ((widget as Widget)?.Platform.Context) + callback(widget, e); + } + } + + internal void Attach(TControl widget) + { + ((IHandler2)(widget as Widget).Handler).AttachEvent(widget, this); + } + + public bool IsSupported + { + get + { + var h = Platform.Instance.CreateShared(typeof(TControl)) as IHandler2; + return h?.SupportsEvent(this) ?? false; + } + } + + } + + public interface IHandler2 + { + void Initialize(object widget); + void AttachEvent(object widget, object evt); + bool TryGetValue(object widget, object prop, out object value); + bool TrySetValue(object widget, object prop, object value); + bool SupportsEvent(object evt); + bool SupportsProperty(object prop); + } + + public class WidgetHandler2 : WidgetHandler2 + where TWidget : Widget + where TControl : new() + { + static object CtlProp = new object(); + + public override void Initialize(TWidget widget) + { + base.Initialize(widget); + SetControl(widget, new TControl()); + } + + public static TControl GetControl(TWidget widget) + { + return widget.Properties.Get(CtlProp); + } + public static void SetControl(TWidget widget, TControl value) + { + widget.Properties.Set(CtlProp, value); + } + } + + public class WidgetHandler2 : IHandler2 + { + public virtual void Initialize(TWidget widget) + { + } + + public virtual Action GetEvent(object evt) + { + return null; + } + + public virtual Func GetProperty(object property) + { + return null; + } + + public virtual Action SetProperty(object property) + { + return null; + } + + static Dictionary> s_events; + static Dictionary> events => s_events ?? (s_events = new Dictionary>()); + static Dictionary> s_getprops; + static Dictionary> getprops => s_getprops ?? (s_getprops = new Dictionary>()); + static Dictionary> s_setprops; + static Dictionary> setprops => s_setprops ?? (s_setprops = new Dictionary>()); + + + void IHandler2.AttachEvent(object widget, object evt) => GetAttachEvent(evt)?.Invoke((TWidget)widget); + + Action GetAttachEvent(object evt) + { + Action del; + if (events.TryGetValue(evt, out del)) + return del; + + del = GetEvent(evt); + events.Add(evt, del); + return del; + } + + bool IHandler2.SupportsEvent(object evt) => GetAttachEvent(evt) != null; + + bool IHandler2.SupportsProperty(object prop) => GetSetProperty(prop) != null || GetGetProperty(prop) != null; + + bool IHandler2.TryGetValue(object widget, object prop, out object value) + { + var getDelegate = GetProperty(prop); + if (getDelegate != null) + { + value = getDelegate((TWidget)widget); + return true; + } + value = null; + return false; + } + + bool IHandler2.TrySetValue(object widget, object prop, object value) + { + var setDelegate = SetProperty(prop); + if (setDelegate != null) + { + setDelegate((TWidget)widget, value); + return true; + } + return false; + } + + Action GetSetProperty(object prop) + { + Action del; + if (setprops.TryGetValue(prop, out del)) + return del; + + del = SetProperty(prop); + setprops.Add(prop, del); + return del; + } + + Func GetGetProperty(object prop) + { + Func del; + if (getprops.TryGetValue(prop, out del)) + return del; + + del = GetProperty(prop); + getprops.Add(prop, del); + return del; + } + + void IHandler2.Initialize(object widget) => Initialize((TWidget)widget); + } +} diff --git a/Source/Eto/Platform.cs b/Source/Eto/Platform.cs index 1273a58d44..5e9bc03f11 100644 --- a/Source/Eto/Platform.cs +++ b/Source/Eto/Platform.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Threading; using Eto.Drawing; +using Eto.Forms; namespace Eto { @@ -429,6 +430,11 @@ public void Add(Func instantiator) Add(typeof(T), instantiator); } + public void Add(Func instantiator) + { + Add(typeof(T), instantiator); + } + /// /// Add the specified type and instantiator. /// @@ -446,20 +452,7 @@ public void Add(Type type, Func instantiator) /// Find the delegate to create instances of the specified /// /// Type of the handler interface to get the instantiator for (usually derived from or another type) - public Func Find(Type type) - { - Func activator; - if (instantiatorMap.TryGetValue(type, out activator)) - return activator; - - var handler = type.GetCustomAttribute(true); - if (handler != null && instantiatorMap.TryGetValue(handler.Type, out activator)) - { - instantiatorMap.Add(type, activator); - return activator; - } - return null; - } + public Func Find(Type type) => FindHandler(type)?.Instantiator; /// /// Finds the delegate to create instances of the specified type @@ -487,6 +480,14 @@ internal HandlerInfo FindHandler(Type type) handlerMap.Add(type, info); return info; } + + if (instantiatorMap.TryGetValue(type, out activator)) + { + info = new HandlerInfo(true, activator); + handlerMap.Add(type, info); + return info; + } + return null; } diff --git a/Source/Eto/PropertyStore.cs b/Source/Eto/PropertyStore.cs index d9e2b510d7..8c141e4168 100644 --- a/Source/Eto/PropertyStore.cs +++ b/Source/Eto/PropertyStore.cs @@ -5,6 +5,7 @@ using System.Windows.Input; using Eto.Forms; using System.Reflection; +using System.Diagnostics; namespace Eto { @@ -60,6 +61,61 @@ bool IsEqual(T existing, T value) return TryGetValue(key, out value) ? (T)value : defaultValue; } + public T Get(DependencyProperty property) + { + var w = Parent as Widget; + object value; + if (w.Handler2.TryGetValue(Parent, property, out value)) + return (T)value; + return (T)Get(property, property.DefaultValue); + } + + public void Set(DependencyProperty property, T value) + { + var w = Parent as Widget; + Set((object)property, value, property.DefaultValue); + if (!w.Handler2.TrySetValue(Parent, property, value)) + { + Debug.WriteLine(@"WARNING: Property {property} could not be set on this platform"); + } + } + + public void AddEvent(DependencyProperty property, Delegate value) + { + var key = property.EventKey; + object existingDelegate; + if (TryGetValue(key, out existingDelegate)) + this[key] = Delegate.Combine((Delegate)existingDelegate, value); + else + { + Add(key, value); + } + } + + public void RemoveEvent(DependencyProperty property, Delegate value) + { + var key = property.EventKey; + object existingDelegate; + if (TryGetValue(key, out existingDelegate)) + { + this[key] = Delegate.Remove((Delegate)existingDelegate, value); + } + } + + public void AddEvent(DependencyEvent evt, Delegate value) + where TArgs : EventArgs + { + object existingDelegate; + if (TryGetValue(evt, out existingDelegate)) + this[evt] = Delegate.Combine((Delegate)existingDelegate, value); + else + { + evt.Attach((TControl)Parent); + Add(evt, value); + } + } + + /// /// Gets a value from the property store with the specified key of a concrete type /// diff --git a/Source/Eto/Widget.cs b/Source/Eto/Widget.cs index 471b1f897f..8ec66aa339 100644 --- a/Source/Eto/Widget.cs +++ b/Source/Eto/Widget.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.Linq.Expressions; +using Eto.Forms; namespace Eto { @@ -83,6 +84,8 @@ public abstract class Widget : IHandlerSource, IDisposable, ICallbackSource /// public object Handler { get; internal set; } + public IHandler2 Handler2 => Handler as IHandler2; + /// /// Gets the native platform-specific handle for integration purposes /// @@ -261,9 +264,8 @@ protected Widget(IHandler handler) /// protected void Initialize() { - var handler = WidgetHandler; - if (handler != null) - handler.Initialize(); + WidgetHandler?.Initialize(); + Handler2?.Initialize(this); EventLookup.HookupEvents(this); Platform.Instance.TriggerWidgetCreated(new WidgetCreatedEventArgs(this)); } diff --git a/Source/Eto/WidgetHandler.cs b/Source/Eto/WidgetHandler.cs index fab46ecfbb..9ddef7ce17 100644 --- a/Source/Eto/WidgetHandler.cs +++ b/Source/Eto/WidgetHandler.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Linq; using System.Diagnostics; +using Eto.Forms; namespace Eto { @@ -27,7 +28,7 @@ namespace Eto /// /// /// Type of widget the handler is for - public abstract class WidgetHandler : Widget.IHandler, IDisposable + public abstract class WidgetHandler : Widget.IHandler, IDisposable, IHandler2 where TWidget: Widget { const string InstanceEventSuffix = ".Instance"; @@ -177,6 +178,97 @@ public void Dispose() protected virtual void Dispose(bool disposing) { } + + public virtual void Initialize(TWidget widget) + { + } + + public virtual Action GetEvent(object evt) + { + return null; + } + + public virtual Func GetProperty(object property) + { + return null; + } + + public virtual Action SetProperty(object property) + { + return null; + } + + static Dictionary> s_events; + static Dictionary> events => s_events ?? (s_events = new Dictionary>()); + static Dictionary> s_getprops; + static Dictionary> getprops => s_getprops ?? (s_getprops = new Dictionary>()); + static Dictionary> s_setprops; + static Dictionary> setprops => s_setprops ?? (s_setprops = new Dictionary>()); + + + void IHandler2.AttachEvent(object widget, object evt) => GetAttachEvent(evt)?.Invoke((TWidget)widget); + + Action GetAttachEvent(object evt) + { + Action del; + if (events.TryGetValue(evt, out del)) + return del; + + del = GetEvent(evt); + events.Add(evt, del); + return del; + } + + bool IHandler2.SupportsEvent(object evt) => GetAttachEvent(evt) != null; + + bool IHandler2.SupportsProperty(object prop) => GetSetProperty(prop) != null || GetGetProperty(prop) != null; + + bool IHandler2.TryGetValue(object widget, object prop, out object value) + { + var getDelegate = GetProperty(prop); + if (getDelegate != null) + { + value = getDelegate((TWidget)widget); + return true; + } + value = null; + return false; + } + + bool IHandler2.TrySetValue(object widget, object prop, object value) + { + var setDelegate = SetProperty(prop); + if (setDelegate != null) + { + setDelegate((TWidget)widget, value); + return true; + } + return false; + } + + Action GetSetProperty(object prop) + { + Action del; + if (setprops.TryGetValue(prop, out del)) + return del; + + del = SetProperty(prop); + setprops.Add(prop, del); + return del; + } + + Func GetGetProperty(object prop) + { + Func del; + if (getprops.TryGetValue(prop, out del)) + return del; + + del = GetProperty(prop); + getprops.Add(prop, del); + return del; + } + + void IHandler2.Initialize(object widget) => Initialize((TWidget)widget); } ///