diff --git a/src/Artemis.Core/Plugins/Plugin.cs b/src/Artemis.Core/Plugins/Plugin.cs index 3ab1a99ef..0b4379a09 100644 --- a/src/Artemis.Core/Plugins/Plugin.cs +++ b/src/Artemis.Core/Plugins/Plugin.cs @@ -107,7 +107,7 @@ namespace Artemis.Core /// If found, the instance of the feature public T? GetFeature() where T : PluginFeature { - return _features.FirstOrDefault(i => i.Instance is T) as T; + return _features.FirstOrDefault(i => i.Instance is T)?.Instance as T; } /// @@ -164,6 +164,11 @@ namespace Artemis.Core } } + internal bool HasEnabledFeatures() + { + return Entity.Features.Any(f => f.IsEnabled) || Features.Any(f => f.AlwaysEnabled); + } + #region IDisposable /// diff --git a/src/Artemis.Core/Plugins/PluginFeatureAttribute.cs b/src/Artemis.Core/Plugins/PluginFeatureAttribute.cs index 0bb78fb75..40863ce25 100644 --- a/src/Artemis.Core/Plugins/PluginFeatureAttribute.cs +++ b/src/Artemis.Core/Plugins/PluginFeatureAttribute.cs @@ -23,5 +23,10 @@ namespace Artemis.Core /// available icons /// public string? Icon { get; set; } + + /// + /// Marks the feature to always be enabled as long as the plugin is enabled + /// + public bool AlwaysEnabled { get; set; } } -} +} \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs index b8515197f..47f34cbaa 100644 --- a/src/Artemis.Core/Plugins/PluginFeatureInfo.cs +++ b/src/Artemis.Core/Plugins/PluginFeatureInfo.cs @@ -28,7 +28,8 @@ namespace Artemis.Core Name = attribute?.Name ?? featureType.Name.Humanize(LetterCasing.Title); Description = attribute?.Description; Icon = attribute?.Icon; - + AlwaysEnabled = attribute?.AlwaysEnabled ?? false; + if (Icon != null) return; if (typeof(BaseDataModelExpansion).IsAssignableFrom(featureType)) Icon = "TableAdd"; @@ -55,6 +56,7 @@ namespace Artemis.Core Name = attribute?.Name ?? instance.GetType().Name.Humanize(LetterCasing.Title); Description = attribute?.Description; Icon = attribute?.Icon; + AlwaysEnabled = attribute?.AlwaysEnabled ?? false; Instance = instance; if (Icon != null) return; @@ -111,6 +113,12 @@ namespace Artemis.Core set => SetAndNotify(ref _icon, value); } + /// + /// Marks the feature to always be enabled as long as the plugin is enabled and cannot be disabled + /// + [JsonProperty] + public bool AlwaysEnabled { get; } + /// /// Gets the feature this info is associated with /// diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index cadb77caf..7840660ac 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -207,7 +207,7 @@ namespace Artemis.Core.Services // ReSharper disable InconsistentlySynchronizedField - It's read-only, idc _logger.Debug("Loaded {count} plugin(s)", _plugins.Count); - bool adminRequired = _plugins.Any(p => p.Info.RequiresAdmin && p.Entity.IsEnabled && p.Entity.Features.Any(f => f.IsEnabled)); + bool adminRequired = _plugins.Any(p => p.Info.RequiresAdmin && p.Entity.IsEnabled && p.HasEnabledFeatures()); if (!isElevated && adminRequired) { _logger.Information("Restarting because one or more plugins requires elevation"); @@ -340,7 +340,7 @@ namespace Artemis.Core.Services if (plugin.Assembly == null) throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded"); - if (plugin.Info.RequiresAdmin && plugin.Entity.Features.Any(f => f.IsEnabled) && !_isElevated) + if (plugin.Info.RequiresAdmin && plugin.HasEnabledFeatures() && !_isElevated) { if (!saveState) throw new ArtemisCoreException("Cannot enable a plugin that requires elevation without saving it's state."); @@ -387,7 +387,7 @@ namespace Artemis.Core.Services } // Activate features after they are all loaded - foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && f.Instance.Entity.IsEnabled)) + foreach (PluginFeatureInfo pluginFeature in plugin.Features.Where(f => f.Instance != null && (f.Instance.Entity.IsEnabled || f.AlwaysEnabled))) { try { diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml index b97f80323..2060c520c 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureView.xaml @@ -50,13 +50,17 @@ VerticalAlignment="Center" Margin="8" Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}" - Orientation="Horizontal"> + Orientation="Horizontal" + ToolTip="This feature cannot be disabled without disabling the whole plugin" + ToolTipService.IsEnabled="{Binding FeatureInfo.AlwaysEnabled}"> - + Feature enabled diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs index fda530a9c..a6a80b341 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginFeatureViewModel.cs @@ -16,6 +16,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins private readonly IPluginManagementService _pluginManagementService; private bool _enabling; private readonly IMessageService _messageService; + private bool _canToggleEnabled; public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield, @@ -50,6 +51,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins set => Task.Run(() => UpdateEnabled(value)); } + public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled; + public void ShowLogsFolder() { try @@ -76,6 +79,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins _pluginManagementService.PluginFeatureEnabled += OnFeatureEnableStopped; _pluginManagementService.PluginFeatureEnableFailed += OnFeatureEnableStopped; + FeatureInfo.Plugin.Enabled += PluginOnToggled; + FeatureInfo.Plugin.Disabled += PluginOnToggled; + base.OnInitialActivate(); } @@ -85,6 +91,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins _pluginManagementService.PluginFeatureEnabled -= OnFeatureEnableStopped; _pluginManagementService.PluginFeatureEnableFailed -= OnFeatureEnableStopped; + FeatureInfo.Plugin.Enabled -= PluginOnToggled; + FeatureInfo.Plugin.Disabled -= PluginOnToggled; + base.OnClose(); } @@ -147,6 +156,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins NotifyOfPropertyChange(nameof(LoadException)); } + private void PluginOnToggled(object sender, EventArgs e) + { + NotifyOfPropertyChange(nameof(CanToggleEnabled)); + } + #endregion } } \ No newline at end of file diff --git a/src/Artemis.sln.DotSettings b/src/Artemis.sln.DotSettings index 3a843dade..bffc7dc38 100644 --- a/src/Artemis.sln.DotSettings +++ b/src/Artemis.sln.DotSettings @@ -1,6 +1,9 @@  + Inherit + True True ERROR + ERROR Built-in: Full Cleanup True NEVER @@ -128,17 +131,6 @@ <Name /> </Entry.SortBy> </Entry> - <Entry Priority="100" DisplayName="Public Enums"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Kind Is="Enum" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> <Entry DisplayName="Static Fields and Constants"> <Entry.Match> <Or> @@ -207,16 +199,41 @@ <ImplementsInterface Immediate="True" /> </Entry.SortBy> </Entry> + <Entry Priority="100" DisplayName="Public Enums"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Enum" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Region Name="Events" /> + <Region Name="Event handlers" /> + <Region Name="IDisposable"> + <Region.GroupBy> + <ImplementsInterface /> + </Region.GroupBy> + </Region> </TypePattern> </Patterns> ERROR ERROR ERROR + True + Replace + True + True True + True False True + False False True + RGB SK UI True