-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC002] Static GProperty Descriptors #57
Comments
I've noticed that we have talked about properties without seeing another important guys, signals. For now I've seen that to register to signals in wrappers we call What do you think ? |
For my idea to make signals works like properties, this is the implementation I'm testing (not pushed on #58 for now) using System;
using System.Collections.Generic;
namespace GObject
{
public class SignalEventArgs : EventArgs
{
public object[] Args { get; }
internal SignalEventArgs(params object[] args)
{
Args = args;
}
internal SignalEventArgs(params Sys.Value[] args)
{
Args = new object[args.Length];
for (int i = 0; i < args.Length; i++)
{
// TODO: Args[i] = args[i].Value;
}
}
}
/// <summary>
/// Describes a GSignal.
/// </summary>
public sealed class Signal
{
#region Fields
private static readonly Dictionary<EventHandler<SignalEventArgs>, ActionRefValues> _handlers = new Dictionary<EventHandler<SignalEventArgs>, ActionRefValues>();
#endregion
#region Properties
/// <summary>
/// The name of the GSignal.
/// </summary>
public string Name { get; }
/// <summary>
/// The signal flags.
/// </summary>
public Sys.SignalFlags Flags { get; }
/// <summary>
/// The return type of signal handlers.
/// </summary>
public Sys.Type ReturnType { get; }
/// <summary>
/// The type of parameters in signal handlers.
/// </summary>
public Sys.Type[] ParamTypes { get; }
#endregion
#region Constructors
private Signal(string name, Sys.SignalFlags flags, Sys.Type returnType, Sys.Type[] paramTypes)
{
Name = name;
Flags = flags;
ReturnType = returnType;
ParamTypes = paramTypes;
}
#endregion
#region Methods
public static Signal Register(string name, Sys.SignalFlags flags, Sys.Type returnType, params Sys.Type[] paramTypes)
{
return new Signal(name, flags, returnType, paramTypes);
}
public static Signal Register(string name, Sys.SignalFlags flags = Sys.SignalFlags.run_last)
{
return new Signal(name, flags, Sys.Type.None, Array.Empty<Sys.Type>());
}
public void Connect(Object o, Action action, bool after = false)
{
if (action == null)
return;
o.RegisterEvent(Name, action, after);
}
public void Connect(Object o, ActionRefValues action, bool after = false)
{
if (action == null)
return;
o.RegisterEvent(Name, action, after);
}
public void Connect(Object o, EventHandler<SignalEventArgs> action, bool after = false)
{
if (action == null)
return;
if (_handlers.TryGetValue(action, out ActionRefValues callback))
return;
callback = (ref Sys.Value[] values) => action(o, new SignalEventArgs(values));
o.RegisterEvent(Name, callback, after);
_handlers[action] = callback;
}
public void Disconnect(Object o, Action action)
{
if (action == null)
return;
o.UnregisterEvent(action);
}
public void Disconnect(Object o, ActionRefValues action)
{
if (action == null)
return;
o.UnregisterEvent(action);
}
public void Disconnect(Object o, EventHandler<SignalEventArgs> action)
{
if (action == null)
return;
if (!_handlers.TryGetValue(action, out ActionRefValues callback))
return;
o.UnregisterEvent(callback);
_handlers.Remove(action);
}
#endregion
}
} And a simple example to use this in a wrapper will be: using System;
using GObject;
namespace Gtk
{
public class Button : Widget
{
public static readonly Signal ClickedSignal = Signal.Register("clicked");
public event EventHandler<SignalEventArgs> Clicked
{
add => ClickedSignal.Connect(this, value, true);
remove => ClickedSignal.Disconnect(this, value);
}
// ...
} @badcel, @firox263 what do you think about the syntax ? It can be ease you the generation of wrappers ? EDIT: The use of this syntax works as expected. |
This looks good to me, we should see how the signals and properties integrate into: #52 |
One thing I would like to try and keep are strongly typed signal args. It makes the developer experience much nicer as it integrates with intellisense, etc For example: class DestroySignalArgs : SignalArgs
{
string someRelevantArg;
int someOtherRelevantArg;
} |
👍 I think you could just try to use the new properties / signals in With this we could check the overall design and decide if this is what we want. |
Good point, but these ones can be generated? Or they will be hand crafted? |
For the proof of concept we can hand craft them. Later we can try to generate as much as possible if there is no API tradeoff. |
While it will be totally possible to do that, It will break the experimental feature I've added in #58 for the definition of signals callbacks using indexers. If we want theses features together, a possible workaround will be to generate theses indexers at the same time we will generate signal descriptors: // In the Container class...
// The signal descriptor will need to know the type of the SignalArgs
public static readonly Signal<AddedSignalArgs> AddSignal = Signal<AddedSignalArgs>.Register("add");
// This one need to be generated, or added manually
public EventHandler<AddedSignalArgs> this[Signal<AddedSignalArgs> signal]
{
set => signal.Connect(this, value, true);
} |
I think it's reasonable to generate this automatically. It's definitely possible as we have the necessary type annotations in Gir data. We should register a normal event handler so we still get the Edit: We should write these manually for now and add them to the generator once we're satisfied on the exact syntax |
@firox263 the complete syntax is this: // The signal descriptor
public static readonly Signal<AddedSignalArgs> AddSignal = Signal<AddedSignalArgs>.Register("add");
// The event handler
public event EventHandler<AddedSignalArgs> Added
{
add => AddSignal.Connect(this, value, true);
remove => AddSignal.Disconnect(this, value);
}
// The signal indexer
public EventHandler<AddedSignalArgs> this[Signal<AddedSignalArgs> signal]
{
set => signal.Connect(this, value, true);
} But I don't think it's possible use the event handler directly into the indexer 🤔 |
That's perfectly fine in my opinion. As long as we support the traditional use case of event handlers as well as the indexer, I don't think there's a problem here. Lgtm 👍 |
Static GProperty Descriptors
RFC
Please see the RFC here: https://github.com/gircore/rfcs/blob/master/rfc-002-static-properties.md
It explains the proposal and some potential solutions.
Summary
To allow Gir.Core be a part of the current C# UI ecosystem, the implementation of GProperties can be done using Static Properties, to match what it's done now using DependencyProperty in other libraries. For that we have the following goals:
These goals can lead us to these achievements:
The text was updated successfully, but these errors were encountered: