preliminary implementation of GTK engine for .NET core

This commit is contained in:
Michael Becker 2024-07-04 21:41:38 -04:00
parent faf0327f6a
commit bb7fdd9291
45 changed files with 1449 additions and 6 deletions

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
namespace MBS.Desktop.Engines.GTK3;
public abstract class GTK3NativeImplementation : NativeImplementation
{
}

View File

@ -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;
}
}

View File

@ -0,0 +1,63 @@
namespace MBS.Desktop.Engines.GTK3.Internal.GIO.Constants;
/// <summary>
/// Flags used to define the behaviour of a <see cref="GApplication" />.
/// </summary>
public enum GApplicationFlags
{
/// <summary>
/// Default. Deprecated in 2.74, use G_APPLICATION_DEFAULT_FLAGS instead.
/// </summary>
None = 0,
/// <summary>
/// Default flags. Since: 2.74
/// </summary>
Default = 0,
/// <summary>
/// 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.
/// </summary>
Service = (1 << 0),
/// <summary>
/// Don't try to become the primary instance.
/// </summary>
Launcher = (1 << 1),
/// <summary>
/// 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.
/// </summary>
HandlesOpen = (1 << 2),
/// <summary>
/// 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.
/// </summary>
HandlesCommandLine = (1 << 3),
/// <summary>
/// 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().
/// </summary>
SendEnvironment = (1 << 4),
/// <summary>
/// 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.
/// </summary>
NonUnique = (1 << 5),
/// <summary>
/// Allow users to override the application ID from the command line with --gapplication-app-id. Since: 2.48
/// </summary>
CanOverrideAppId = (1 << 6),
/// <summary>
/// Allow another instance to take over the bus name. Since: 2.60
/// </summary>
AllowReplacement = (1 << 7),
/// <summary>
/// Take over from another instance. This flag is usually set by passing --gapplication-replace on the commandline. Since: 2.60
/// </summary>
Replace = (1 << 8)
}

View File

@ -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);
}

View File

@ -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;
/// <summary>
/// The base object type
/// </summary>
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<T>(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);
}
}

View File

@ -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)
{
}
}

View File

@ -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);
}

View File

@ -0,0 +1,8 @@
namespace MBS.Desktop.Engines.GTK3.Internal.GObject.Constants;
public enum GConnectFlags
{
Default,
After,
Swapped
}

View File

@ -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);
}

View File

@ -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);
}
/// <summary>
/// This function does the same work as <see cref="Init" /> with only a single
/// change: It does not terminate the program if the commandline arguments
/// couldnt be parsed or the windowing system cant be initialized.
/// </summary>
/// <returns>
/// <see cref="string[]" /> of command-line arguments after the call to gtk_init_check,
/// or <see cref="null" /> if gtk_init_check returned FALSE.
/// </returns>
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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Classes;
public class GtkBin : GtkContainer
{
}

View File

@ -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); }
}
}

View File

@ -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);
}
}

View File

@ -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<bool>("use-markup"); }
set { SetProperty("use-markup", value); }
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,7 @@
namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
public enum GtkOrientation
{
Horizontal,
Vertical
}

View File

@ -0,0 +1,7 @@
namespace MBS.Desktop.Engines.GTK3.Internal.GTK.Constants;
public enum GtkWindowType
{
Toplevel,
Popup
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\lib\MBS.Desktop\MBS.Desktop.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="MBS.Desktop.Engines.GTK3.dll.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<dllmap dll="appindicator" target="libappindicator3.so.1" />
<dllmap dll="gtk-x11-2.0" target="libgtk-x11-2.0.so.0" />
<dllmap dll="gtk-3" target="libgtk-3.so.0" />
<dllmap dll="gtk-4" target="libgtk-4.so.1" />
<dllmap dll="gdk-3" target="libgdk-3.so.0" />
<dllmap dll="notify" target="libnotify.so.4" />
<dllmap dll="gio-2.0" target="libgio-2.0.so.0" />
<dllmap dll="glib-2.0" target="libglib-2.0.so.0" />
<dllmap dll="gobject-2.0" target="libgobject-2.0.so.0" />
<dllmap dll="pango-1.0" target="libpango-1.0.so.0" />
<dllmap dll="cairo" target="libcairo.so.2" />
<dllmap dll="libgdl-3" target="libgdl-3.so.5" />
<dllmap dll="libgtksourceview-3.0" target="libgtksourceview-3.0.so.1" />
</configuration>

View File

@ -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));
}
}
}

View File

@ -0,0 +1,29 @@
namespace MBS.Desktop.Controls;
public class Control : Implementable
{
public class ControlCollection : System.Collections.ObjectModel.Collection<Control>
{
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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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<DesktopApplicationEngine>();
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)

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
namespace MBS.Desktop;
public interface IImplementable
{
Implementation? Implementation { get; }
bool IsCreated { get; }
void CreateImplementation();
}

View File

@ -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<Implementation>();
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);
}
}
}

View File

@ -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;
}
}

View File

@ -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) { }
}

View File

@ -0,0 +1,11 @@
namespace MBS.Desktop;
public class ImplementsForAttribute : Attribute
{
public Type Type { get; }
public ImplementsForAttribute(Type type)
{
Type = type;
}
}

View File

@ -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<Implementation>();
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);
}
}
}

View File

@ -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;
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\MBS.Core\MBS.Core.csproj" />
<ProjectReference Include="..\..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Core\MBS.Core.csproj" />
</ItemGroup>
<PropertyGroup>

View File

@ -0,0 +1,5 @@
namespace MBS.Desktop;
public abstract class NativeHandle
{
}

View File

@ -0,0 +1,6 @@
namespace MBS.Desktop;
public abstract class NativeImplementation : Implementation
{
}