diff --git a/Pilz.UI.AvaloniaUI.Features/Extensions.cs b/Pilz.UI.AvaloniaUI.Features/Extensions.cs new file mode 100644 index 0000000..35ec1bd --- /dev/null +++ b/Pilz.UI.AvaloniaUI.Features/Extensions.cs @@ -0,0 +1,145 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Pilz.Features; + +namespace Pilz.UI.AvaloniaUI.Features; + +public static class Extensions +{ + public static MenuItem GetAsItem(this PluginModuleBase module) + { + return module.GetAsItem(true); + } + + public static MenuItem GetAsItem(this PluginModuleBase module, bool addDefaultHandler) + { + return module.GetAsItem(addDefaultHandler ? RadMenuItem_RMMethod_Click : 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 ? RadMenuItem_RMFunction_Click : null); + } + + public static MenuItem GetAsItem(this PluginFeature module, EventHandler? clickHandler) + { + var item = new MenuItem + { + Header = module.Name, + Icon = module.Icon as Image, + Tag = module, + IsVisible = module.Enabled, + //ToolTipText = module.Description ?? module.Name, + }; + + if (clickHandler is not null) + item.Click += clickHandler; + + return item; + } + + /// + /// Inserts all items to an item collection. + /// + /// + /// + /// 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 how and where the items will be inserted. + /// 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, + ItemCollection itemsCollection, + bool addDefaultHandler = false, + EventHandler? customClickHandler = null, + FeatureInsertMode insertMode = FeatureInsertMode.Default, + int? customDefault = null, + int? customTop = null, + int? customBottom = null, + bool insertPrioSplitters = false) + { + var insertDefault = customDefault ?? (insertMode.HasFlag(FeatureInsertMode.DefaultStart) ? 0 : itemsCollection.Count); + var insertTop = customTop ?? (insertMode.HasFlag(FeatureInsertMode.InsertTop) || insertMode.HasFlag(FeatureInsertMode.DefaultStart) ? 0 : insertDefault); + var insertBottom = customBottom ?? (insertMode.HasFlag(FeatureInsertMode.InsertBottom) || insertMode.HasFlag(FeatureInsertMode.DefaultEnd) ? itemsCollection.Count : insertDefault); + 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.Click += customClickHandler; + + if (insertPrioSplitters && prevPrio > feature.Prioritization) + { + if (prevPrio != null) + insertItem(new Separator()); + prevPrio = feature.Prioritization; + } + + insertItem(item); + + void insertItem(object item) + { + switch (feature.Prioritization) + { + case >= FeaturePrioritization.High: + if (insertDefault >= insertTop) insertDefault++; + if (insertBottom >= insertTop) insertBottom++; + // ... + itemsCollection.Insert(insertTop++, item); + break; + case FeaturePrioritization.Default: + if (insertBottom >= insertDefault) insertBottom++; + if (insertTop >= insertDefault) insertTop++; + // ... + itemsCollection.Insert(insertDefault++, item); + break; + case <= FeaturePrioritization.Low: + if (insertTop >= insertBottom) insertTop++; + if (insertDefault >= insertBottom) insertDefault++; + // ... + itemsCollection.Insert(insertBottom++, item); + break; + } + } + + if (item.Parent != null) + insertedItems.Add(item); + } + + return insertedItems; + } + + private static void RadMenuItem_RMMethod_Click(object? sender, RoutedEventArgs e) + { + //if (sender is MenuItem item && item.IsEnabled && item.Tag is PluginModule module) + // module.ShowUI(); + } + + private static void RadMenuItem_RMFunction_Click(object? sender, RoutedEventArgs e) + { + if (sender is MenuItem item && item.Tag is PluginFunction function && function.Enabled) + function.Execute(); + } +} diff --git a/Pilz.UI.AvaloniaUI.Features/FeatureInsertMode.cs b/Pilz.UI.AvaloniaUI.Features/FeatureInsertMode.cs new file mode 100644 index 0000000..d04099c --- /dev/null +++ b/Pilz.UI.AvaloniaUI.Features/FeatureInsertMode.cs @@ -0,0 +1,33 @@ +using Pilz.Features; + +namespace Pilz.UI.AvaloniaUI.Features; + +[Flags] +public enum FeatureInsertMode +{ + /// + /// Features will be inserted at the end of the collection. + /// + Default = 0, + /// + /// Features will be inserted at the end of the collection. + /// This is the default behavior and equals . + /// + DefaultEnd = Default, + /// + /// Features will be inserted at the start of the collection. + /// + DefaultStart = 1, + /// + /// Features with prioritization will be inserted at the top (or left). + /// + InsertTop = 1 << 2, + /// + /// Features with prioritization will be inserted at the bottom (or right). + /// + InsertBottom = 1 << 3, + /// + /// Features with prioritization other then will be inserted at the top or bottom. + /// + InsertTopAndBottom = InsertTop | InsertBottom, +} diff --git a/Pilz.UI.AvaloniaUI.Features/FeatureInsertPosition.cs b/Pilz.UI.AvaloniaUI.Features/FeatureInsertPosition.cs new file mode 100644 index 0000000..ba50738 --- /dev/null +++ b/Pilz.UI.AvaloniaUI.Features/FeatureInsertPosition.cs @@ -0,0 +1,11 @@ +namespace Pilz.UI.AvaloniaUI.Features; + +[Flags] +public enum FeatureInsertPosition +{ + None = 0, + Default = 1, + Top = 2, + Bottom = 3, + All = int.MaxValue, +} diff --git a/Pilz.UI.AvaloniaUI.Features/Pilz.UI.AvaloniaUI.Features.csproj b/Pilz.UI.AvaloniaUI.Features/Pilz.UI.AvaloniaUI.Features.csproj new file mode 100644 index 0000000..abecdde --- /dev/null +++ b/Pilz.UI.AvaloniaUI.Features/Pilz.UI.AvaloniaUI.Features.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + + + + 1.0.0 + + + + + + + + + + + diff --git a/Pilz.sln b/Pilz.sln index 5097624..13da3f1 100644 --- a/Pilz.sln +++ b/Pilz.sln @@ -53,6 +53,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pilz.UI.AvaloniaUI.Symbols" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pilz.UI.AvaloniaUI", "Pilz.UI.AvaloniaUI\Pilz.UI.AvaloniaUI.csproj", "{919DC195-50C2-48AE-9885-94897B3DC5B4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pilz.UI.AvaloniaUI.Features", "Pilz.UI.AvaloniaUI.Features\Pilz.UI.AvaloniaUI.Features.csproj", "{E6607C0C-FD8D-422A-A612-B359F092FFAE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -261,6 +263,14 @@ Global {919DC195-50C2-48AE-9885-94897B3DC5B4}.Release|Any CPU.Build.0 = Release|Any CPU {919DC195-50C2-48AE-9885-94897B3DC5B4}.Release|x86.ActiveCfg = Release|Any CPU {919DC195-50C2-48AE-9885-94897B3DC5B4}.Release|x86.Build.0 = Release|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Debug|x86.ActiveCfg = Debug|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Debug|x86.Build.0 = Debug|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Release|Any CPU.Build.0 = Release|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Release|x86.ActiveCfg = Release|Any CPU + {E6607C0C-FD8D-422A-A612-B359F092FFAE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE