diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/ContainerImplementation.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/ContainerImplementation.cs
new file mode 100644
index 0000000..7518461
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/ContainerImplementation.cs
@@ -0,0 +1,49 @@
+using MBS.Core.Drawing;
+using MBS.Desktop.Controls;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.ControlImplementations;
+
+public abstract class ContainerImplementation : GTK3NativeImplementation, Container.IImplementation
+{
+ public void InsertControl(Control control, int index)
+ {
+ Container container = (Container)Control;
+ GtkContainer native = (GtkContainer)((GTK3NativeHandle)container.Layout.Implementation.Handle).Handle;
+
+ control.CreateImplementation();
+
+
+ //!FIXME: we need to use the Layout here
+ native.Add(((GTK3NativeHandle)control.Implementation.Handle).Handle);
+ }
+
+ public void RemoveControl(Control control)
+ {
+ GtkContainer container = (GtkContainer)((GTK3NativeHandle)Handle).Handle;
+ container.Remove(((GTK3NativeHandle)control.Implementation.Handle).Handle);
+ }
+
+ public void RemoveAllControls()
+ {
+ }
+
+ protected override void OnNativeHandleCreated(EventArgs e)
+ {
+ base.OnNativeHandleCreated(e);
+
+ Container container = (Container)Control;
+ Layout layout = container.Layout;
+
+ GTK3NativeHandle nhControl = (GTK3NativeHandle)Handle;
+
+ layout.CreateImplementation();
+ GTK3NativeHandle nhLayout = (GTK3NativeHandle)layout.Implementation.Handle;
+
+ GtkContainer ct = (GtkContainer)nhControl.Handle;
+ ct.Add(nhLayout.Handle);
+ }
+
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/LabelImplementation.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/LabelImplementation.cs
new file mode 100644
index 0000000..149c522
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/LabelImplementation.cs
@@ -0,0 +1,33 @@
+using MBS.Core.Drawing;
+using MBS.Desktop.Controls;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.ControlImplementations;
+
+[ImplementsFor(typeof(Label))]
+public class LabelImplementation : GTK3NativeImplementation, Label.IImplementation
+{
+ protected override NativeHandle CreateNativeHandle()
+ {
+ return new GTK3NativeHandle(new GtkLabel());
+ }
+ public string GetText()
+ {
+ return ((GtkLabel)((GTK3NativeHandle)Handle).Handle).Text;
+ }
+
+ public void SetText(string value)
+ {
+ ((GtkLabel)((GTK3NativeHandle)Handle).Handle).Text = value;
+ }
+ public bool GetUseMarkup()
+ {
+ return ((GtkLabel)((GTK3NativeHandle)Handle).Handle).UseMarkup;
+ }
+
+ public void SetUseMarkup(bool value)
+ {
+ ((GtkLabel)((GTK3NativeHandle)Handle).Handle).UseMarkup = value;
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/WindowImplementation.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/WindowImplementation.cs
new file mode 100644
index 0000000..c43dbe2
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/ControlImplementations/WindowImplementation.cs
@@ -0,0 +1,65 @@
+using MBS.Core.Drawing;
+using MBS.Desktop.Controls;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.ControlImplementations;
+
+[ImplementsFor(typeof(Window))]
+public class WindowImplementation : ContainerImplementation, Window.IImplementation
+{
+ public void Show()
+ {
+ GtkWindow window = (GtkWindow)((GTK3NativeHandle)Handle).Handle;
+ window.ShowAll();
+ }
+ public bool GetVisible()
+ {
+ GtkWindow window = (GtkWindow)((GTK3NativeHandle)Handle).Handle;
+ return window.IsVisible;
+ }
+ public void SetVisible(bool value)
+ {
+ GtkWindow window = (GtkWindow)((GTK3NativeHandle)Handle).Handle;
+ if (value)
+ {
+ window.ShowAll();
+ }
+ else
+ {
+ window.Hide();
+ }
+ }
+
+ public string GetTitle()
+ {
+ GtkWindow window = (GtkWindow)((GTK3NativeHandle)Handle).Handle;
+ return window.Title;
+ }
+ public void SetTitle(string value)
+ {
+ GtkWindow window = (GtkWindow)((GTK3NativeHandle)Handle).Handle;
+ window.Title = value;
+ }
+
+ public Dimension2D GetDefaultSize()
+ {
+ GtkWindow window = (GtkWindow)((GTK3NativeHandle)Handle).Handle;
+ int width = 0, height = 0;
+ window.GetDefaultSize(out width, out height);
+
+ return new Dimension2D(width, height);
+ }
+ public void SetDefaultSize(Dimension2D value)
+ {
+ GtkWindow window = (GtkWindow)((GTK3NativeHandle)Handle).Handle;
+ window.SetDefaultSize((int)value.Width, (int)value.Height);
+ }
+
+ protected override NativeHandle CreateNativeHandle()
+ {
+ Window window = (Window)Control;
+ GtkWindow native = new GtkWindow(((GTK3DesktopApplicationEngine)((DesktopApplication)DesktopApplication.Instance).Engine).ApplicationHandle);
+ return new GTK3NativeHandle(native);
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3DesktopApplicationEngine.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3DesktopApplicationEngine.cs
new file mode 100644
index 0000000..4b2938f
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3DesktopApplicationEngine.cs
@@ -0,0 +1,70 @@
+namespace MBS.Desktop.Engines.GTK3;
+
+using MBS.Core;
+using MBS.Core.Reflection;
+using MBS.Desktop.Controls;
+using MBS.Desktop.Engines.GTK3.Internal.GIO.Constants;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+using static MBS.Desktop.Engines.GTK3.Internal.GObject.Delegates;
+
+public class GTK3DesktopApplicationEngine : DesktopApplicationEngine
+{
+ protected string GetApplicationId()
+ {
+ return "net.alcetech.framework.desktop__" + this.GetType().FullName;
+ }
+
+ public GtkApplication? ApplicationHandle { get; private set; }
+
+ protected override void InitializeInternal()
+ {
+ ApplicationHandle = new GtkApplication(GetApplicationId(), GApplicationFlags.HandlesCommandLine);
+ }
+
+ Action2P startup_d;
+ Action2P activate_d;
+ Action3P commandline_d;
+
+ public GTK3DesktopApplicationEngine()
+ {
+ startup_d = new Action2P(startup);
+ activate_d = new Action2P(activate);
+ commandline_d = new Action3P(commandline);
+ }
+
+ private void activate(IntPtr application, IntPtr user_data)
+ {
+ // Invoker.Invoke(Application.Instance, "OnStartup", new object[] { EventArgs.Empty });
+ Console.WriteLine("activate() called , we should be using commandline ()");
+ }
+ private void startup(IntPtr application, IntPtr user_data)
+ {
+ Console.WriteLine("startup() called");
+ Invoker.Invoke(Application.Instance, "OnStartup", new object[] { EventArgs.Empty });
+ }
+
+ private static bool _firstRun = true;
+ private void commandline(IntPtr handle, IntPtr commandLine, IntPtr data)
+ {
+ Console.WriteLine("commandline() called");
+
+ int argc = 0;
+ IntPtr hwpp = Internal.GIO.Methods.g_application_command_line_get_arguments(commandLine, ref argc);
+
+ string[] arguments = MBS.Core.Interop.InteropServices.PtrToStringArray(hwpp, argc);
+
+ CommandLine cline = new CommandLine(arguments);
+ ApplicationActivatedEventArgs e = new ApplicationActivatedEventArgs(_firstRun, ApplicationActivationType.Launch, cline);
+ Invoker.Invoke(Application.Instance, "OnActivated", new object[] { e });
+ }
+
+ protected override int StartInternal()
+ {
+ ApplicationHandle.Connect("startup", startup_d);
+ ApplicationHandle.Connect("activate", activate_d);
+ ApplicationHandle.Connect("command_line", commandline_d);
+
+ int retval = ApplicationHandle.Run();
+ return retval;
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3NativeHandle.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3NativeHandle.cs
new file mode 100644
index 0000000..71bbd69
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3NativeHandle.cs
@@ -0,0 +1,14 @@
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+namespace MBS.Desktop.Engines.GTK3;
+
+public class GTK3NativeHandle : NativeHandle
+{
+ public GtkWidget Handle { get; private set; }
+
+ public GTK3NativeHandle(GtkWidget handle)
+ {
+ Handle = handle;
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3NativeImplementation.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3NativeImplementation.cs
new file mode 100644
index 0000000..69eb814
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/GTK3NativeImplementation.cs
@@ -0,0 +1,6 @@
+namespace MBS.Desktop.Engines.GTK3;
+
+public abstract class GTK3NativeImplementation : NativeImplementation
+{
+
+}
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Classes/GApplication.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Classes/GApplication.cs
new file mode 100644
index 0000000..593c6bb
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Classes/GApplication.cs
@@ -0,0 +1,18 @@
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GIO.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GIO.Classes;
+
+public class GApplication : GObject.Classes.GObject
+{
+ [DllImport(LIBRARY_FILENAME_GIO)]
+ private static extern int g_application_run(IntPtr /*GApplication*/ application, int argc, string[] argv);
+
+ public int Run()
+ {
+ string[] argv = Environment.GetCommandLineArgs();
+ int argc = argv.Length;
+ int retval = g_application_run(Handle, argc, argv);
+ return retval;
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Constants/GApplicationFlags.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Constants/GApplicationFlags.cs
new file mode 100644
index 0000000..1484439
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Constants/GApplicationFlags.cs
@@ -0,0 +1,63 @@
+namespace MBS.Desktop.Engines.GTK3.Internal.GIO.Constants;
+
+///
+/// Flags used to define the behaviour of a .
+///
+public enum GApplicationFlags
+{
+ ///
+ /// Default. Deprecated in 2.74, use G_APPLICATION_DEFAULT_FLAGS instead.
+ ///
+ None = 0,
+ ///
+ /// Default flags. Since: 2.74
+ ///
+ Default = 0,
+ ///
+ /// Run as a service. In this mode, registration fails if the service is already running, and the application will initially wait up to 10 seconds for an initial activation message to arrive.
+ ///
+ Service = (1 << 0),
+ ///
+ /// Don't try to become the primary instance.
+ ///
+ Launcher = (1 << 1),
+ ///
+ /// This application handles opening files (in the primary instance). Note that this flag
+ /// only affects the default implementation of local_command_line(), and has no effect if
+ /// G_APPLICATION_HANDLES_COMMAND_LINE is given. See g_application_run() for details.
+ ///
+ HandlesOpen = (1 << 2),
+ ///
+ /// This application handles command line arguments (in the primary instance). Note that
+ /// this flag only affect the default implementation of local_command_line(). See
+ /// g_application_run() for details.
+ ///
+ HandlesCommandLine = (1 << 3),
+ ///
+ /// Send the environment of the launching process to the primary instance. Set this flag
+ /// if your application is expected to behave differently depending on certain environment
+ /// variables. For instance, an editor might be expected to use the GIT_COMMITTER_NAME
+ /// environment variable when editing a git commit message. The environment is available to
+ /// the "command-line" signal handler, via g_application_command_line_getenv().
+ ///
+ SendEnvironment = (1 << 4),
+ ///
+ /// Make no attempts to do any of the typical single-instance application negotiation,
+ /// even if the application ID is given. The application neither attempts to become the
+ /// owner of the application ID nor does it check if an existing owner already exists.
+ /// Everything occurs in the local process. Since: 2.30.
+ ///
+ NonUnique = (1 << 5),
+ ///
+ /// Allow users to override the application ID from the command line with --gapplication-app-id. Since: 2.48
+ ///
+ CanOverrideAppId = (1 << 6),
+ ///
+ /// Allow another instance to take over the bus name. Since: 2.60
+ ///
+ AllowReplacement = (1 << 7),
+ ///
+ /// Take over from another instance. This flag is usually set by passing --gapplication-replace on the commandline. Since: 2.60
+ ///
+ Replace = (1 << 8)
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Methods.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Methods.cs
new file mode 100644
index 0000000..bb0df29
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GIO/Methods.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GIO;
+
+internal static class Methods
+{
+ public const string LIBRARY_FILENAME_GIO = "gio-2.0";
+
+ [DllImport(LIBRARY_FILENAME_GIO)]
+ public static extern IntPtr g_application_command_line_get_arguments(IntPtr command_line, ref int argc);
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GObject.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GObject.cs
new file mode 100644
index 0000000..d7d6db1
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GObject.cs
@@ -0,0 +1,106 @@
+using System.Linq.Expressions;
+using System.Reflection.Metadata.Ecma335;
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GObject.Constants;
+using static MBS.Desktop.Engines.GTK3.Internal.GObject.Delegates;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GObject.Classes;
+
+///
+/// The base object type
+///
+public class GObject
+{
+ public const string LIBRARY_FILENAME_GTK = "gtk-3";
+ public const string LIBRARY_FILENAME_GLIB = "glib-2.0";
+ public const string LIBRARY_FILENAME_GOBJECT = "gobject-2.0";
+ public const string LIBRARY_FILENAME_GIO = "gio-2.0";
+
+ internal GObject(IntPtr handle)
+ {
+ Handle = handle;
+ }
+ protected GObject()
+ {
+ }
+
+ public IntPtr Handle { get; protected set; }
+
+ // private static extern void g_object_set_property (IntPtr obj, string property_name, ref GValue value);
+ [DllImport(LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_object_get_property (IntPtr obj, string property_name, IntPtr value);
+ [DllImport(LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_object_set_property (IntPtr obj, string property_name, IntPtr value);
+
+ public T GetProperty(string property_name)
+ {
+ if (typeof(T) == typeof(bool))
+ {
+ GValue value = new GValue(GValueType.Boolean);
+ g_object_get_property(Handle, property_name, value.Handle);
+
+ bool val = value.GetBoolean();
+ return (T)((object)val);
+ }
+ else if (typeof(T) == typeof(IntPtr))
+ {
+ GValue value = new GValue(GValueType.Pointer);
+ g_object_get_property(Handle, property_name, value.Handle);
+ IntPtr val = value.GetPointer();
+ return (T)((object)val);
+ }
+ throw new NotImplementedException();
+ }
+ public void SetProperty(string property_name, object value)
+ {
+ if (value is bool)
+ {
+ GValue gvalue = new GValue(GValueType.Boolean);
+ gvalue.SetBoolean((bool)value);
+ g_object_set_property(Handle, property_name, gvalue.Handle);
+ }
+ else if (value is IntPtr)
+ {
+ GValue gvalue = new GValue(GValueType.Pointer);
+ gvalue.SetPointer((IntPtr)value);
+ g_object_set_property(Handle, property_name, gvalue.Handle);
+ }
+ else if (value is GObject)
+ {
+ GValue gvalue = new GValue(GValueType.Object);
+ gvalue.SetObject((GObject)value);
+ g_object_set_property(Handle, property_name, gvalue.Handle);
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ [DllImport(LIBRARY_FILENAME_GOBJECT)]
+ private static extern IntPtr g_type_from_name (string name);
+
+
+ [DllImport(LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_signal_connect_data(IntPtr instance, string detailed_signal, Action c_handler, IntPtr data, IntPtr destroy_data, GConnectFlags connect_flags);
+ [DllImport(LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_signal_connect_data(IntPtr instance, string detailed_signal, Action1P c_handler, IntPtr data, IntPtr destroy_data, GConnectFlags connect_flags);
+ [DllImport(LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_signal_connect_data(IntPtr instance, string detailed_signal, Action2P c_handler, IntPtr data, IntPtr destroy_data, GConnectFlags connect_flags);
+ [DllImport(LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_signal_connect_data(IntPtr instance, string detailed_signal, Action3P c_handler, IntPtr data, IntPtr destroy_data, GConnectFlags connect_flags);
+
+ public void Connect(string detailed_signal, Action1P c_handler, IntPtr data = default(IntPtr))
+ {
+ g_signal_connect_data(Handle, detailed_signal, c_handler, data, IntPtr.Zero, GConnectFlags.Default);
+ }
+ public void Connect(string detailed_signal, Action2P c_handler, IntPtr data = default(IntPtr))
+ {
+ g_signal_connect_data(Handle, detailed_signal, c_handler, data, IntPtr.Zero, GConnectFlags.Default);
+ }
+ public void Connect(string detailed_signal, Action3P c_handler, IntPtr data = default(IntPtr))
+ {
+ g_signal_connect_data(Handle, detailed_signal, c_handler, data, IntPtr.Zero, GConnectFlags.Default);
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GValue.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GValue.cs
new file mode 100644
index 0000000..33c3796
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GValue.cs
@@ -0,0 +1,80 @@
+using System.Runtime.InteropServices;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GObject.Classes;
+
+public class GValue
+{
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern IntPtr g_value_init(IntPtr gvalue, IntPtr gtype);
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern IntPtr g_value_get_object(IntPtr gvalue);
+
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_value_set_object(IntPtr gvalue, IntPtr ptr);
+
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern IntPtr g_value_get_pointer(IntPtr gvalue);
+
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_value_set_pointer(IntPtr gvalue, IntPtr ptr);
+
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern long g_value_get_int64(IntPtr gvalue);
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_value_set_int64(IntPtr gvalue, long value);
+
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern bool g_value_get_boolean(IntPtr gvalue);
+ [DllImport(GObject.LIBRARY_FILENAME_GOBJECT)]
+ private static extern void g_value_set_boolean(IntPtr gvalue, bool value);
+
+ private const int G_VALUE_SIZE = 24; // found by calling sizeof(G_VALUE_INIT) in C
+ private IntPtr g_value_new(IntPtr gtype)
+ {
+ IntPtr gvalue = Marshal.AllocHGlobal(G_VALUE_SIZE);
+ for (int i = 0; i < G_VALUE_SIZE; i++)
+ {
+ Marshal.WriteByte(gvalue, i, 0);
+ }
+
+ Console.WriteLine("g_value_new: initializing new GValue of type {0}", gtype);
+ IntPtr gvalue2 = g_value_init(gvalue, gtype);
+ return gvalue2;
+ }
+
+ public IntPtr Handle { get; }
+
+ public GValue(GValueType type)
+ {
+ Handle = g_value_new(type.Handle);
+ }
+
+ public bool GetBoolean()
+ {
+ return g_value_get_boolean(Handle);
+ }
+ public void SetBoolean(bool value)
+ {
+ g_value_set_boolean(Handle, value);
+ }
+
+ public IntPtr GetPointer()
+ {
+ return g_value_get_pointer(Handle);
+ }
+ public void SetPointer(IntPtr value)
+ {
+ g_value_set_pointer(Handle, value);
+ }
+
+ public GObject GetObject()
+ {
+ IntPtr h = g_value_get_object(Handle);
+ // !FIXME: look up in our table to see if we have a .NET object for this
+ return new GObject(h);
+ }
+ public void SetObject(GObject value)
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GValueType.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GValueType.cs
new file mode 100644
index 0000000..b0f1f98
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Classes/GValueType.cs
@@ -0,0 +1,30 @@
+namespace MBS.Desktop.Engines.GTK3.Internal.GObject.Classes;
+
+public class GValueType
+{
+ private const int G_TYPE_FUNDAMENTAL_SHIFT = 2;
+
+ private static IntPtr G_TYPE_MAKE_FUNDAMENTAL(int x)
+ {
+ return new IntPtr(x << G_TYPE_FUNDAMENTAL_SHIFT);
+ }
+
+ public IntPtr Handle { get; }
+ public GValueType(IntPtr handle)
+ {
+ Handle = handle;
+ }
+
+ private static readonly IntPtr G_TYPE_BOOLEAN = G_TYPE_MAKE_FUNDAMENTAL(5);
+ public static GValueType Boolean { get; } = new GValueType(G_TYPE_BOOLEAN);
+
+ private static readonly IntPtr G_TYPE_INT64 = G_TYPE_MAKE_FUNDAMENTAL(10);
+ public static GValueType Int64 { get; } = new GValueType(G_TYPE_INT64);
+
+ private static readonly IntPtr G_TYPE_POINTER = G_TYPE_MAKE_FUNDAMENTAL(17);
+ public static GValueType Pointer { get; } = new GValueType(G_TYPE_POINTER);
+
+ private static readonly IntPtr G_TYPE_OBJECT = G_TYPE_MAKE_FUNDAMENTAL(20);
+ public static GValueType Object { get; } = new GValueType(G_TYPE_OBJECT);
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Constants/GConnectFlags.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Constants/GConnectFlags.cs
new file mode 100644
index 0000000..af96351
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Constants/GConnectFlags.cs
@@ -0,0 +1,8 @@
+namespace MBS.Desktop.Engines.GTK3.Internal.GObject.Constants;
+
+public enum GConnectFlags
+{
+ Default,
+ After,
+ Swapped
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Delegates.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Delegates.cs
new file mode 100644
index 0000000..d63e41f
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GObject/Delegates.cs
@@ -0,0 +1,13 @@
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GObject.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GObject;
+
+public static class Delegates
+{
+
+ public delegate void Action1P(IntPtr parm1);
+ public delegate void Action2P(IntPtr parm1, IntPtr parm2);
+ public delegate void Action3P(IntPtr parm1, IntPtr parm2, IntPtr parm3);
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/Gtk.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/Gtk.cs
new file mode 100644
index 0000000..ecec138
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/Gtk.cs
@@ -0,0 +1,38 @@
+using System.Runtime.InteropServices;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+public static class Gtk
+{
+ [DllImport("gtk-3")]
+ private static extern void gtk_init(ref int argc, ref string[] argv);
+ [DllImport("gtk-3")]
+ private static extern bool gtk_init_check(ref int argc, ref string[] argv);
+
+ public static void Init()
+ {
+ string[] argv = System.Environment.GetCommandLineArgs();
+ int argc = argv.Length;
+ gtk_init(ref argc, ref argv);
+ }
+ ///
+ /// This function does the same work as with only a single
+ /// change: It does not terminate the program if the commandline arguments
+ /// couldn’t be parsed or the windowing system can’t be initialized.
+ ///
+ ///
+ /// of command-line arguments after the call to gtk_init_check,
+ /// or if gtk_init_check returned FALSE.
+ ///
+ public static string[] InitCheck()
+ {
+ string[] argv = System.Environment.GetCommandLineArgs();
+ int argc = argv.Length;
+ bool value = gtk_init_check(ref argc, ref argv);
+ if (value)
+ {
+ return argv;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkApplication.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkApplication.cs
new file mode 100644
index 0000000..84984e8
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkApplication.cs
@@ -0,0 +1,30 @@
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GIO.Classes;
+using MBS.Desktop.Engines.GTK3.Internal.GIO.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+public class GtkApplication : GApplication
+{
+ public string Id { get; }
+ public GApplicationFlags Flags { get; }
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern IntPtr gtk_application_new(string application_id, GApplicationFlags flags);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern IntPtr gtk_application_add_window(IntPtr application, IntPtr window);
+
+ public void AddWindow(GtkWindow window)
+ {
+ gtk_application_add_window(Handle, window.Handle);
+ }
+
+ public GtkApplication(string id, GApplicationFlags flags)
+ {
+ Console.WriteLine("creating new GtkApplication with id {0}", id);
+ Handle = gtk_application_new(id, flags);
+
+ Id = id;
+ Flags = flags;
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkBin.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkBin.cs
new file mode 100644
index 0000000..174bd2b
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkBin.cs
@@ -0,0 +1,6 @@
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+public class GtkBin : GtkContainer
+{
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkBox.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkBox.cs
new file mode 100644
index 0000000..457d9c5
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkBox.cs
@@ -0,0 +1,45 @@
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Interfaces;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+public class GtkBox : GtkContainer, GtkOrientable
+{
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern IntPtr /*GtkBox*/ gtk_box_new(GtkOrientation orientation, int spacing);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_box_pack_start(IntPtr /*GtkBox*/ handle, IntPtr /*GtkWidget*/ child, bool expand, bool fill, uint padding);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_box_pack_end(IntPtr /*GtkBox*/ handle, IntPtr /*GtkWidget*/ child, bool expand, bool fill, uint padding);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern bool gtk_box_get_homogenous(IntPtr /*GtkBox*/ handle);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_box_set_homogenous(IntPtr /*GtkBox*/ handle, bool value);
+
+ public GtkBox(GtkOrientation orientation, int spacing)
+ {
+ Handle = gtk_box_new(orientation, spacing);
+ }
+
+ public void PackStart(GtkWidget child, bool expand, bool fill, uint padding)
+ {
+ gtk_box_pack_start(Handle, child.Handle, expand, fill, padding);
+ }
+ public void PackEnd(GtkWidget child, bool expand, bool fill, uint padding)
+ {
+ gtk_box_pack_end(Handle, child.Handle, expand, fill, padding);
+ }
+
+ public bool Homogenous
+ {
+ get { return gtk_box_get_homogenous(Handle); }
+ set { gtk_box_set_homogenous(Handle, value); }
+ }
+
+ public GtkOrientation Orientation
+ {
+ get { return GtkOrientable.gtk_orientable_get_orientation(Handle); }
+ set { GtkOrientable.gtk_orientable_set_orientation(Handle, value); }
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkContainer.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkContainer.cs
new file mode 100644
index 0000000..82e3802
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkContainer.cs
@@ -0,0 +1,22 @@
+using System.Runtime.InteropServices;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+public class GtkContainer : GtkWidget
+{
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_container_add(IntPtr /*GtkContainer*/ container, IntPtr /*GtkWidget*/ widget);
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_container_remove(IntPtr /*GtkContainer*/ container, IntPtr /*GtkWidget*/ widget);
+
+ public void Add(GtkWidget widget)
+ {
+ gtk_container_add(Handle, widget.Handle);
+ }
+ public void Remove(GtkWidget widget)
+ {
+ gtk_container_remove(Handle, widget.Handle);
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkLabel.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkLabel.cs
new file mode 100644
index 0000000..5e26996
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkLabel.cs
@@ -0,0 +1,31 @@
+using System.Runtime.InteropServices;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+public class GtkLabel : GtkWidget
+{
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern IntPtr gtk_label_new (string str);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern IntPtr gtk_label_set_label (IntPtr /*GtkLabel*/ label, string str);
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern string gtk_label_get_label (IntPtr /*GtkLabel*/ label);
+
+ public GtkLabel()
+ {
+ Handle = gtk_label_new("Hello World!");
+ }
+
+ public string Text
+ {
+ get { return gtk_label_get_label(Handle); }
+ set { gtk_label_set_label(Handle, value); }
+ }
+ public bool UseMarkup
+ {
+ get { return GetProperty("use-markup"); }
+ set { SetProperty("use-markup", value); }
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkWidget.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkWidget.cs
new file mode 100644
index 0000000..6740aa6
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkWidget.cs
@@ -0,0 +1,46 @@
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GObject.Classes;
+
+public class GtkWidget : GObject
+{
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_widget_show(IntPtr widget);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_widget_show_all(IntPtr widget);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_widget_hide(IntPtr widget);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_widget_destroy(IntPtr widget);
+
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern bool gtk_widget_is_visible(IntPtr widget);
+
+ public bool IsVisible
+ {
+ get
+ {
+ return gtk_widget_is_visible(Handle);
+ }
+ }
+
+ public void Show()
+ {
+ gtk_widget_show(Handle);
+ }
+ public void ShowAll()
+ {
+ gtk_widget_show_all(Handle);
+ }
+ public void Hide()
+ {
+ gtk_widget_hide(Handle);
+ }
+ public void Destroy()
+ {
+ gtk_widget_destroy(Handle);
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkWindow.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkWindow.cs
new file mode 100644
index 0000000..1ffc1f5
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Classes/GtkWindow.cs
@@ -0,0 +1,60 @@
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+
+public class GtkWindow : GtkBin
+{
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern IntPtr gtk_window_new(GtkWindowType type);
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern IntPtr gtk_application_window_new(IntPtr application);
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_window_get_default_size(IntPtr window, ref int width, ref int height);
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_window_set_default_size(IntPtr window, int width, int height);
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern void gtk_window_set_title(IntPtr window, string title);
+
+ [DllImport(LIBRARY_FILENAME_GTK)]
+ private static extern string gtk_window_get_title(IntPtr window);
+
+ public string Title
+ {
+ get
+ {
+ return gtk_window_get_title(Handle);
+ }
+ set
+ {
+ gtk_window_set_title(Handle, value);
+ }
+ }
+
+ public GtkWindow(GtkWindowType type)
+ {
+ Handle = gtk_window_new(type);
+ }
+ public GtkWindow(GtkApplication application)
+ {
+ Handle = gtk_application_window_new(application.Handle);
+ }
+
+ public void GetDefaultSize(out int width, out int height)
+ {
+ int w = 0, h = 0;
+ gtk_window_get_default_size(Handle, ref w, ref h);
+
+ width = w;
+ height = h;
+ }
+ public void SetDefaultSize(int width, int height)
+ {
+ gtk_window_set_default_size(Handle, width, height);
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Constants/GtkOrientation.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Constants/GtkOrientation.cs
new file mode 100644
index 0000000..4fa30c1
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Constants/GtkOrientation.cs
@@ -0,0 +1,7 @@
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+
+public enum GtkOrientation
+{
+ Horizontal,
+ Vertical
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Constants/GtkWindowType.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Constants/GtkWindowType.cs
new file mode 100644
index 0000000..d241cb1
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Constants/GtkWindowType.cs
@@ -0,0 +1,7 @@
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+
+public enum GtkWindowType
+{
+ Toplevel,
+ Popup
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Interfaces/GtkOrientable.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Interfaces/GtkOrientable.cs
new file mode 100644
index 0000000..dcdfe0d
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/Internal/GTK/Interfaces/GtkOrientable.cs
@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+
+namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Interfaces;
+
+public interface GtkOrientable
+{
+ GtkOrientation Orientation { get; set; }
+
+ [DllImport(GObject.Classes.GObject.LIBRARY_FILENAME_GTK)]
+ public static extern GtkOrientation gtk_orientable_get_orientation(IntPtr /*GtkOrientable*/ orientable);
+ [DllImport(GObject.Classes.GObject.LIBRARY_FILENAME_GTK)]
+ public static extern void gtk_orientable_set_orientation(IntPtr /*GtkOrientable*/ orientable, GtkOrientation value);
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/LayoutImplementations/BoxLayoutImplementation.cs b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/LayoutImplementations/BoxLayoutImplementation.cs
new file mode 100644
index 0000000..09f0645
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/LayoutImplementations/BoxLayoutImplementation.cs
@@ -0,0 +1,47 @@
+using MBS.Core;
+using MBS.Core.Drawing;
+using MBS.Desktop.Controls;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
+using MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
+using MBS.Desktop.Layouts;
+
+namespace MBS.Desktop.Engines.GTK3.LayoutImplementations;
+
+[ImplementsFor(typeof(BoxLayout))]
+public class BoxLayoutImplementation : GTK3NativeImplementation, BoxLayout.IImplementation
+{
+ protected override NativeHandle CreateNativeHandle()
+ {
+ return new GTK3NativeHandle(new GtkBox(GtkOrientation.Horizontal, 0));
+ }
+
+ public Orientation GetOrientation()
+ {
+ GtkBox box = (GtkBox) ((GTK3NativeHandle)Handle).Handle;
+ switch (box.Orientation)
+ {
+ case GtkOrientation.Horizontal: return Orientation.Horizontal;
+ case GtkOrientation.Vertical: return Orientation.Vertical;
+ }
+ throw new NotSupportedException();
+ }
+ public void SetOrientation(Orientation value)
+ {
+ GtkBox box = (GtkBox) ((GTK3NativeHandle)Handle).Handle;
+ switch (value)
+ {
+ case Orientation.Horizontal:
+ {
+ box.Orientation = GtkOrientation.Horizontal;
+ return;
+ }
+ case Orientation.Vertical:
+ {
+ box.Orientation = GtkOrientation.Vertical;
+ return;
+ }
+ }
+ throw new NotSupportedException();
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/MBS.Desktop.Engines.GTK3.csproj b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/MBS.Desktop.Engines.GTK3.csproj
new file mode 100644
index 0000000..6dfc2b7
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/MBS.Desktop.Engines.GTK3.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+ net8.0
+ enable
+ enable
+
+
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/MBS.Desktop.Engines.GTK3.dll.config b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/MBS.Desktop.Engines.GTK3.dll.config
new file mode 100644
index 0000000..5830065
--- /dev/null
+++ b/desktop-framework-dotnet/src/engines/MBS.Desktop.Engines.GTK3/MBS.Desktop.Engines.GTK3.dll.config
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Container.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Container.cs
new file mode 100644
index 0000000..c3a45b7
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Container.cs
@@ -0,0 +1,50 @@
+using System.ComponentModel;
+
+namespace MBS.Desktop.Controls;
+
+public abstract class Container : Control
+{
+ public interface IImplementation
+ {
+ void InsertControl(Control control, int index);
+ void RemoveControl(Control control);
+ void RemoveAllControls();
+ }
+
+ private Layout _Layout = null;
+ public Layout Layout
+ {
+ get { return _Layout; }
+ set
+ {
+ _Layout = value;
+ }
+ }
+ public Control.ControlCollection Controls { get; }
+
+ protected override void OnNativeHandleCreating(CancelEventArgs e)
+ {
+ base.OnNativeHandleCreating(e);
+
+ // ensure the layout is created first
+ if (Layout != null)
+ {
+ Layout.CreateImplementation();
+ }
+ }
+
+ public Container()
+ {
+ Controls = new Control.ControlCollection(this);
+ }
+
+ protected override void InitializeImplementedProperties()
+ {
+ base.InitializeImplementedProperties();
+
+ foreach (Control ctl in Controls)
+ {
+ (Implementation as Container.IImplementation).InsertControl(ctl, Controls.IndexOf(ctl));
+ }
+ }
+}
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Control.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Control.cs
new file mode 100644
index 0000000..92591fa
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Control.cs
@@ -0,0 +1,29 @@
+namespace MBS.Desktop.Controls;
+
+public class Control : Implementable
+{
+ public class ControlCollection : System.Collections.ObjectModel.Collection
+ {
+ private Container _parent;
+ public ControlCollection(Container parent)
+ {
+ _parent = parent;
+ }
+
+ protected override void ClearItems()
+ {
+ ((Container.IImplementation?)_parent.Implementation)?.RemoveAllControls();
+ base.ClearItems();
+ }
+ protected override void InsertItem(int index, Control item)
+ {
+ base.InsertItem(index, item);
+ ((Container.IImplementation?)_parent.Implementation)?.InsertControl(item, index);
+ }
+ protected override void RemoveItem(int index)
+ {
+ ((Container.IImplementation?)_parent.Implementation)?.RemoveControl(this[index]);
+ base.RemoveItem(index);
+ }
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Label.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Label.cs
new file mode 100644
index 0000000..5c175f3
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Label.cs
@@ -0,0 +1,65 @@
+namespace MBS.Desktop.Controls;
+
+public class Label : Control
+{
+ public interface IImplementation
+ {
+ string GetText();
+ void SetText(string value);
+
+ bool GetUseMarkup();
+ void SetUseMarkup(bool value);
+ }
+
+ public Label()
+ {
+ }
+ public Label(string text)
+ {
+ this.Text = text;
+ }
+
+ protected override void InitializeImplementedProperties()
+ {
+ base.InitializeImplementedProperties();
+
+ Text = _Text;
+ UseMarkup = _UseMarkup;
+ }
+
+ private string _Text = null;
+ public string Text
+ {
+ get
+ {
+ if (Implementation is IImplementation impl)
+ {
+ _Text = impl.GetText();
+ }
+ return _Text;
+ }
+ set
+ {
+ _Text = value;
+ (Implementation as IImplementation)?.SetText(value);
+ }
+ }
+
+ private bool _UseMarkup = false;
+ public bool UseMarkup
+ {
+ get
+ {
+ if (Implementation is IImplementation impl)
+ {
+ _UseMarkup = impl.GetUseMarkup();
+ }
+ return _UseMarkup;
+ }
+ set
+ {
+ _UseMarkup = value;
+ (Implementation as IImplementation)?.SetUseMarkup(value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/MainWindow.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/MainWindow.cs
new file mode 100644
index 0000000..ce708af
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/MainWindow.cs
@@ -0,0 +1,12 @@
+using MBS.Core;
+using MBS.Desktop.Layouts;
+
+namespace MBS.Desktop.Controls;
+
+public class MainWindow : Window
+{
+ public MainWindow()
+ {
+ Layout = new BoxLayout(Orientation.Vertical);
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Window.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Window.cs
new file mode 100644
index 0000000..a282afe
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Controls/Window.cs
@@ -0,0 +1,71 @@
+using MBS.Core.Drawing;
+
+namespace MBS.Desktop.Controls;
+
+public class Window : Container
+{
+ public new interface IImplementation : Container.IImplementation
+ {
+ bool GetVisible();
+ void SetVisible(bool value);
+
+ string GetTitle();
+ void SetTitle(string title);
+
+ Dimension2D GetDefaultSize();
+ void SetDefaultSize(Dimension2D size);
+
+ void Show();
+ }
+
+ public void Show()
+ {
+ CreateImplementation();
+ ((Window.IImplementation)Implementation).Show();
+ }
+
+ protected override void InitializeImplementedProperties()
+ {
+ base.InitializeImplementedProperties();
+
+ Title = _Title;
+ DefaultSize = _DefaultSize;
+ }
+
+ private string? _Title;
+ public string? Title
+ {
+ get
+ {
+ string? title = (Implementation as Window.IImplementation)?.GetTitle();
+ if (title is null)
+ return _Title;
+
+ return title;
+ }
+ set
+ {
+ _Title = value;
+ (Implementation as Window.IImplementation)?.SetTitle(value);
+ }
+ }
+
+ private Dimension2D _DefaultSize = new Dimension2D(-1, -1);
+ public Dimension2D DefaultSize
+ {
+ get
+ {
+ Dimension2D? value = (Implementation as Window.IImplementation)?.GetDefaultSize();
+ if (value is null)
+ return _DefaultSize;
+
+ return value;
+ }
+ set
+ {
+ _DefaultSize = value;
+ (Implementation as Window.IImplementation)?.SetDefaultSize(value);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/DesktopApplication.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/DesktopApplication.cs
index e9c96fe..caa859e 100644
--- a/desktop-framework-dotnet/src/lib/MBS.Desktop/DesktopApplication.cs
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/DesktopApplication.cs
@@ -1,14 +1,34 @@
-namespace MBS.Desktop;
+using MBS.Desktop.Controls;
+
+namespace MBS.Desktop;
public class DesktopApplication : MBS.Core.Application
{
- public DesktopApplicationEngine Engine { get; }
+ public DesktopApplicationEngine Engine { get; private set; }
protected override int StartInternal()
{
- // find the appropriate DesktopApplicationEngine for this platform
-
- return base.StartInternal();
+ Console.WriteLine("DesktopApplication::StartInternal");
+
+ // find the appropriate DesktopApplicationEngine for this platform
+ DesktopApplicationEngine[] engines = MBS.Core.Reflection.TypeLoader.GetAvailableTypes();
+ Console.WriteLine("DesktopApplicationEngine loader: found {0} engines", engines.Length);
+ if (engines.Length > 0)
+ {
+ DesktopApplicationEngine engine = engines[0];
+ if (engine != null)
+ {
+ Engine = engine;
+
+ Console.WriteLine("Using engine '{0}'", engine.GetType().FullName);
+ return engine.Start();
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("no engines were found or could be loaded");
+ }
+ return 2;
}
protected override void StopInternal(int exitCode = 0)
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/DesktopApplicationEngine.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/DesktopApplicationEngine.cs
new file mode 100644
index 0000000..919f8b7
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/DesktopApplicationEngine.cs
@@ -0,0 +1,28 @@
+namespace MBS.Desktop;
+
+public abstract class DesktopApplicationEngine
+{
+ protected abstract void InitializeInternal();
+
+ private bool _Initialized = false;
+ public bool Initialize()
+ {
+ if (_Initialized)
+ return false;
+
+ InitializeInternal();
+ return true;
+ }
+
+ public int Start()
+ {
+ Initialize();
+ return StartInternal();
+ }
+
+ protected virtual int StartInternal()
+ {
+ return 0;
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/IImplementable.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/IImplementable.cs
new file mode 100644
index 0000000..9b30ab7
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/IImplementable.cs
@@ -0,0 +1,8 @@
+namespace MBS.Desktop;
+
+public interface IImplementable
+{
+ Implementation? Implementation { get; }
+ bool IsCreated { get; }
+ void CreateImplementation();
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Implementable.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Implementable.cs
new file mode 100644
index 0000000..3fe6fa3
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Implementable.cs
@@ -0,0 +1,62 @@
+using System.ComponentModel;
+
+namespace MBS.Desktop;
+
+public abstract class Implementable : IImplementable
+{
+ public Implementation? Implementation { get; protected set; }
+
+ public bool IsCreated { get; private set; }
+
+ protected virtual void InitializeImplementedProperties()
+ {
+ }
+
+ protected virtual void OnNativeHandleCreating(CancelEventArgs e)
+ {
+ }
+ protected virtual void OnNativeHandleCreated(EventArgs e)
+ {
+ }
+ protected virtual void OnCreated(EventArgs e)
+ {
+ }
+
+ public void CreateImplementation()
+ {
+ if (Implementation == null)
+ {
+ // determine which Implementation to use to create this Control
+ Implementation[] impls = Core.Reflection.TypeLoader.GetAvailableTypes();
+ foreach (Implementation impl in impls)
+ {
+ if (impl.Supports(GetType()))
+ {
+ Implementation = impl;
+ break;
+ }
+ }
+ }
+ if (!IsCreated)
+ {
+ if (Implementation == null)
+ {
+ throw new ImplementationNotFoundException();
+ }
+
+ CancelEventArgs ce = new CancelEventArgs();
+ OnNativeHandleCreating(ce);
+ if (ce.Cancel)
+ return;
+
+ Implementation.Create(this);
+ IsCreated = true;
+
+ InitializeImplementedProperties();
+ OnNativeHandleCreated(EventArgs.Empty);
+
+ OnCreated(EventArgs.Empty);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Implementation.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Implementation.cs
new file mode 100644
index 0000000..6528184
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Implementation.cs
@@ -0,0 +1,42 @@
+using MBS.Desktop.Controls;
+
+namespace MBS.Desktop;
+
+public abstract class Implementation
+{
+ public object Control { get; private set; }
+ public NativeHandle Handle { get; private set; }
+
+ protected abstract NativeHandle CreateNativeHandle();
+
+ public void Create(IImplementable control)
+ {
+ Control = control;
+ Handle = CreateNativeHandle();
+
+ OnNativeHandleCreated(EventArgs.Empty);
+ OnCreated(EventArgs.Empty);
+ }
+
+ protected virtual void OnNativeHandleCreated(EventArgs e)
+ {
+ }
+ protected virtual void OnCreated(EventArgs e)
+ {
+ }
+
+ public bool Supports(Type typ)
+ {
+ Type typImpl = this.GetType();
+ object[] attrs = typImpl.GetCustomAttributes(false);
+ foreach (object attr in attrs)
+ {
+ if (attr is ImplementsForAttribute ifa)
+ {
+ if (ifa.Type == typ || typ.IsSubclassOf(ifa.Type))
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/ImplementationNotFoundException.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/ImplementationNotFoundException.cs
new file mode 100644
index 0000000..af8853f
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/ImplementationNotFoundException.cs
@@ -0,0 +1,9 @@
+namespace MBS.Desktop;
+
+public class ImplementationNotFoundException : Exception
+{
+
+ public ImplementationNotFoundException() : base("No implementation for the specified IImplementable could be found") { }
+ public ImplementationNotFoundException(string message) : base(message) { }
+ public ImplementationNotFoundException(string message, Exception innerException) : base(message, innerException) { }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/ImplementsForAttribute.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/ImplementsForAttribute.cs
new file mode 100644
index 0000000..0e6b364
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/ImplementsForAttribute.cs
@@ -0,0 +1,11 @@
+namespace MBS.Desktop;
+
+public class ImplementsForAttribute : Attribute
+{
+ public Type Type { get; }
+
+ public ImplementsForAttribute(Type type)
+ {
+ Type = type;
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Layout.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Layout.cs
new file mode 100644
index 0000000..615dca6
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Layout.cs
@@ -0,0 +1,40 @@
+namespace MBS.Desktop;
+
+public abstract class Layout : IImplementable
+{
+ public interface IImplementation
+ {
+
+ }
+
+ public Implementation? Implementation { get; private set; }
+ public bool IsCreated { get; private set; }
+ public virtual void CreateImplementation()
+ {
+ if (Implementation == null)
+ {
+ // determine which Implementation to use to create this Control
+ Implementation[] impls = Core.Reflection.TypeLoader.GetAvailableTypes();
+ foreach (Implementation impl in impls)
+ {
+ if (impl.Supports(GetType()))
+ {
+ Implementation = impl;
+ break;
+ }
+ }
+ }
+ if (!IsCreated)
+ {
+ if (Implementation == null)
+ {
+ throw new ImplementationNotFoundException();
+ }
+ Implementation.Create(this);
+ IsCreated = true;
+
+ // InitializeControlProperties();
+ // OnCreated(EventArgs.Empty);
+ }
+ }
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/Layouts/BoxLayout.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/Layouts/BoxLayout.cs
new file mode 100644
index 0000000..4294fcd
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/Layouts/BoxLayout.cs
@@ -0,0 +1,35 @@
+namespace MBS.Desktop.Layouts;
+
+using MBS.Core;
+
+public class BoxLayout : Layout
+{
+ public new interface IImplementation : Layout.IImplementation
+ {
+ Orientation GetOrientation();
+ void SetOrientation(Orientation value);
+ }
+
+ private Orientation _Orientation = Orientation.Horizontal;
+ public Orientation Orientation
+ {
+ get
+ {
+ if (Implementation is BoxLayout.IImplementation impl)
+ {
+ return impl.GetOrientation();
+ }
+ return _Orientation;
+ }
+ set
+ {
+ (Implementation as BoxLayout.IImplementation)?.SetOrientation(value);
+ _Orientation = value;
+ }
+ }
+
+ public BoxLayout(Orientation orientation = Orientation.Horizontal)
+ {
+ Orientation = orientation;
+ }
+}
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/MBS.Desktop.csproj b/desktop-framework-dotnet/src/lib/MBS.Desktop/MBS.Desktop.csproj
index 020ce4d..a73dd10 100644
--- a/desktop-framework-dotnet/src/lib/MBS.Desktop/MBS.Desktop.csproj
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/MBS.Desktop.csproj
@@ -1,7 +1,7 @@
-
+
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/NativeHandle.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/NativeHandle.cs
new file mode 100644
index 0000000..9345a40
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/NativeHandle.cs
@@ -0,0 +1,5 @@
+namespace MBS.Desktop;
+
+public abstract class NativeHandle
+{
+}
\ No newline at end of file
diff --git a/desktop-framework-dotnet/src/lib/MBS.Desktop/NativeImplementation.cs b/desktop-framework-dotnet/src/lib/MBS.Desktop/NativeImplementation.cs
new file mode 100644
index 0000000..f3642af
--- /dev/null
+++ b/desktop-framework-dotnet/src/lib/MBS.Desktop/NativeImplementation.cs
@@ -0,0 +1,6 @@
+namespace MBS.Desktop;
+
+public abstract class NativeImplementation : Implementation
+{
+
+}
\ No newline at end of file