diff --git a/Pilz.Plugins.Advanced.UI.Gtk/Extensions.cs b/Pilz.Plugins.Advanced.UI.Gtk/Extensions.cs
new file mode 100644
index 0000000..9839bc7
--- /dev/null
+++ b/Pilz.Plugins.Advanced.UI.Gtk/Extensions.cs
@@ -0,0 +1,127 @@
+using Gtk;
+
+namespace Pilz.Plugins.Advanced.UI.Gtk;
+
+public static class Extensions
+{
+ private class PluginFunctionEventArgs(EventArgs original, PluginFunction function) : EventArgs
+ {
+ public EventArgs Original { get; } = original;
+ public PluginFunction Function { get; } = function;
+ }
+
+ private class PluginModuleEventArgs(EventArgs original, PluginModuleBase function) : EventArgs
+ {
+ public EventArgs Original { get; } = original;
+ public PluginModuleBase Function { get; } = function;
+ }
+
+ public static MenuItem GetAsItem(this PluginModuleBase module)
+ {
+ return module.GetAsItem(true);
+ }
+
+ public static MenuItem GetAsItem(this PluginModuleBase module, bool addDefaultHandler)
+ {
+ return module.GetAsItem(addDefaultHandler ? (sender, e) => RadMenuItem_RMMethod_Click(sender, new PluginModuleEventArgs(e, module)) : null);
+ }
+
+ public static MenuItem GetAsItem(this PluginFunction function)
+ {
+ return function.GetAsItem(true);
+ }
+
+ public static MenuItem GetAsItem(this PluginFunction function, bool addDefaultHandler)
+ {
+ return function.GetAsItem(addDefaultHandler ? (sender, e) => RadMenuItem_RMFunction_Click(sender, new PluginFunctionEventArgs(e, function)) : null);
+ }
+
+ public static MenuItem GetAsItem(this PluginFeature module, EventHandler? clickHandler)
+ {
+ var item = new MenuItem(module.Name)
+ {
+ Visible = module.Enabled,
+ TooltipText = module.Description ?? module.Name
+ };
+
+ if (clickHandler is not null)
+ item.Activated += clickHandler;
+
+ return item;
+ }
+
+ ///
+ /// Inserts all items to a menu item.
+ ///
+ ///
+ ///
+ /// Will add a default click handler that executes the feature.
+ /// You usually don't set customClickHandler if you set this parameter to true.
+ /// Adds a custom click handler. If addDefaultHandler is true, it will only work on s.
+ /// You usually don't set addDefaultHandler to true if you set this parameter to something not null.
+ /// Defines a custom default position (index).
+ /// Defines a custom top position (index).
+ /// Defines a custom bottom position (index).
+ /// Defines if splitters should be inserted to seperate the new items by priorization.
+ public static IEnumerable> InsertItemsTo(this IEnumerable features,
+ MenuItem parentMenuItem,
+ bool addDefaultHandler = false,
+ EventHandler? customClickHandler = null,
+ int? customDefault = null,
+ int? customTop = null,
+ int? customBottom = null,
+ bool insertPrioSplitters = false)
+ {
+ var insertedItems = new List>();
+ FeaturePrioritization? prevPrio = null;
+
+ // Oder by priorization
+ features = features.OrderByDescending(n => n.Prioritization);
+
+ foreach (var feature in features)
+ {
+ MenuItem item;
+
+ if (feature is PluginFunction function)
+ item = function.GetAsItem(addDefaultHandler);
+ else if (feature is PluginModuleBase module)
+ item = module.GetAsItem(addDefaultHandler);
+ else
+ item = feature.GetAsItem(null);
+
+ if (!addDefaultHandler && customClickHandler != null)
+ item.Activated += customClickHandler;
+
+ if (insertPrioSplitters && (prevPrio == null || prevPrio > feature.Prioritization))
+ {
+ insertItem(new SeparatorMenuItem());
+ prevPrio = feature.Prioritization;
+ }
+
+ insertItem(item);
+
+ void insertItem(MenuItem item)
+ {
+ if (parentMenuItem.Submenu is Menu subMenu)
+ subMenu.Append(item);
+ }
+
+ if (item.Parent != null)
+ insertedItems.Add(new(item, feature));
+ }
+
+ return insertedItems;
+ }
+
+ private static void RadMenuItem_RMMethod_Click(object? sender, EventArgs e)
+ {
+ if (sender is MenuItem item && item.Sensitive && e is PluginFunctionEventArgs args && args.Function.Enabled)
+ throw new NotImplementedException();
+ }
+
+ private static void RadMenuItem_RMFunction_Click(object? sender, EventArgs e)
+ {
+ if (sender is MenuItem item && item.Sensitive && e is PluginFunctionEventArgs args && args.Function.Enabled)
+ args.Function.Execute();
+ }
+}
\ No newline at end of file
diff --git a/Pilz.Plugins.Advanced.UI.Gtk/Pilz.Plugins.Advanced.UI.Gtk.csproj b/Pilz.Plugins.Advanced.UI.Gtk/Pilz.Plugins.Advanced.UI.Gtk.csproj
new file mode 100644
index 0000000..e9f3589
--- /dev/null
+++ b/Pilz.Plugins.Advanced.UI.Gtk/Pilz.Plugins.Advanced.UI.Gtk.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+ 1.0.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Pilz.sln b/Pilz.sln
index 5e47d7b..a04d9c0 100644
--- a/Pilz.sln
+++ b/Pilz.sln
@@ -51,6 +51,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pilz.UI.Gtk", "Pilz.UI.Gtk\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pilz.UI", "Pilz.UI\Pilz.UI.csproj", "{E75FBCEF-B971-4036-85D3-27D4ACA77156}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pilz.Plugins.Advanced.UI.Gtk", "Pilz.Plugins.Advanced.UI.Gtk\Pilz.Plugins.Advanced.UI.Gtk.csproj", "{F9CA6B5C-F14F-418D-9617-6007ED75E82F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -251,6 +253,14 @@ Global
{E75FBCEF-B971-4036-85D3-27D4ACA77156}.Release|Any CPU.Build.0 = Release|Any CPU
{E75FBCEF-B971-4036-85D3-27D4ACA77156}.Release|x86.ActiveCfg = Release|Any CPU
{E75FBCEF-B971-4036-85D3-27D4ACA77156}.Release|x86.Build.0 = Release|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Debug|x86.Build.0 = Debug|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Release|x86.ActiveCfg = Release|Any CPU
+ {F9CA6B5C-F14F-418D-9617-6007ED75E82F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE