// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using FlaxEditor.GUI; using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; namespace FlaxEditor.Windows { /// /// Editor tool window for plugins management using . /// /// public sealed class PluginsWindow : EditorWindow { private Tabs _tabs; private readonly List _categories = new List(); private readonly Dictionary _entries = new Dictionary(); /// /// Plugin entry control. /// /// public class PluginEntry : ContainerControl { /// /// The plugin. /// public readonly Plugin Plugin; /// /// The category. /// public readonly CategoryEntry Category; /// /// Initializes a new instance of the class. /// /// The plugin. /// The category. /// Plugin description public PluginEntry(Plugin plugin, CategoryEntry category, ref PluginDescription desc) { Plugin = plugin; Category = category; float margin = 4; float iconSize = 64; var iconImage = new Image { Brush = new SpriteBrush(Editor.Instance.Icons.Plugin64), Parent = this, Bounds = new Rectangle(margin, margin, iconSize, iconSize), }; var icon = PluginUtils.TryGetPluginIcon(plugin); if (icon) iconImage.Brush = new TextureBrush(icon); Size = new Vector2(300, 100); float tmp1 = iconImage.Right + margin; var nameLabel = new Label { HorizontalAlignment = TextAlignment.Near, AnchorPreset = AnchorPresets.HorizontalStretchTop, Text = desc.Name, Font = new FontReference(Style.Current.FontLarge), Parent = this, Bounds = new Rectangle(tmp1, margin, Width - tmp1 - margin, 28), }; tmp1 = nameLabel.Bottom + margin + 8; var descLabel = new Label { HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Near, Wrapping = TextWrapping.WrapWords, AnchorPreset = AnchorPresets.HorizontalStretchTop, Text = desc.Description, Parent = this, Bounds = new Rectangle(nameLabel.X, tmp1, nameLabel.Width, Height - tmp1 - margin), }; string versionString = string.Empty; if (desc.IsAlpha) versionString = "ALPHA "; else if (desc.IsBeta) versionString = "BETA "; versionString += "Version "; versionString += desc.Version != null ? desc.Version.ToString() : "1.0"; var versionLabel = new Label { HorizontalAlignment = TextAlignment.Far, VerticalAlignment = TextAlignment.Center, AnchorPreset = AnchorPresets.TopRight, Text = versionString, Parent = this, Bounds = new Rectangle(Width - 140 - margin, margin, 140, 14), }; string url = null; if (!string.IsNullOrEmpty(desc.AuthorUrl)) url = desc.AuthorUrl; else if (!string.IsNullOrEmpty(desc.HomepageUrl)) url = desc.HomepageUrl; else if (!string.IsNullOrEmpty(desc.RepositoryUrl)) url = desc.RepositoryUrl; versionLabel.Font.Font.WaitForLoaded(); var font = versionLabel.Font.GetFont(); var authorWidth = font.MeasureText(desc.Author).X + 8; var authorLabel = new ClickableLabel { HorizontalAlignment = TextAlignment.Far, VerticalAlignment = TextAlignment.Center, AnchorPreset = AnchorPresets.TopRight, Text = desc.Author, Parent = this, Bounds = new Rectangle(Width - authorWidth - margin, versionLabel.Bottom + margin, authorWidth, 14), }; if (url != null) { authorLabel.TextColorHighlighted = Style.Current.BackgroundSelected; authorLabel.DoubleClick = () => Platform.OpenUrl(url); } } } /// /// Plugins category control. /// /// public class CategoryEntry : Tab { /// /// The panel for the plugin entries. /// public VerticalPanel Panel; /// /// Initializes a new instance of the class. /// /// The text. public CategoryEntry(string text) : base(text) { var scroll = new Panel(ScrollBars.Vertical) { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, Parent = this, }; var panel = new VerticalPanel { AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = Margin.Zero, IsScrollable = true, Parent = scroll, }; Panel = panel; } } /// /// Initializes a new instance of the class. /// /// The editor. public PluginsWindow(Editor editor) : base(editor, true, ScrollBars.None) { Title = "Plugins"; _tabs = new Tabs { Orientation = Orientation.Vertical, AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, TabsSize = new Vector2(120, 32), Parent = this }; // Check already loaded plugins PluginManager.GamePlugins.ForEach(OnPluginLoaded); PluginManager.EditorPlugins.ForEach(OnPluginLoaded); // Register for events PluginManager.PluginLoaded += OnPluginLoaded; PluginManager.PluginUnloading += OnPluginUnloading; } private void OnPluginLoaded(Plugin plugin) { var entry = GetPluginEntry(plugin); if (entry != null) return; // Special case for editor plugins (merge with game plugin if has linked) if (plugin is EditorPlugin editorPlugin && GetPluginEntry(editorPlugin.GamePluginType) != null) return; var desc = plugin.Description; var category = _categories.Find(x => string.Equals(x.Text, desc.Category, StringComparison.OrdinalIgnoreCase)); if (category == null) { category = new CategoryEntry(desc.Category) { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, Parent = _tabs }; _categories.Add(category); category.UnlockChildrenRecursive(); } entry = new PluginEntry(plugin, category, ref desc) { Parent = category.Panel, }; _entries.Add(plugin, entry); entry.UnlockChildrenRecursive(); if (_tabs.SelectedTab == null) _tabs.SelectedTab = category; } private void OnPluginUnloading(Plugin plugin) { var entry = GetPluginEntry(plugin); if (entry == null) return; var category = entry.Category; _entries.Remove(plugin); entry.Dispose(); // If category is not used anymore if (_entries.Values.Count(x => x.Category == category) == 0) { if (_tabs.SelectedTab == category) _tabs.SelectedTab = null; category.Dispose(); _categories.Remove(category); if (_tabs.SelectedTab == null && _categories.Count > 0) _tabs.SelectedTab = _categories[0]; } } /// /// Gets the plugin entry control. /// /// The plugin. /// The entry. public PluginEntry GetPluginEntry(Plugin plugin) { _entries.TryGetValue(plugin, out var e); return e; } /// /// Gets the plugin entry control. /// /// The plugin type. /// The entry. public PluginEntry GetPluginEntry(Type pluginType) { if (pluginType == null) return null; foreach (var e in _entries.Keys) { if (e.GetType() == pluginType) return _entries[e]; } return null; } /// public override void OnDestroy() { PluginManager.PluginLoaded -= OnPluginLoaded; PluginManager.PluginUnloading -= OnPluginUnloading; base.OnDestroy(); } } }