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