using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Timers; using Artemis.Core; using Artemis.Core.Modules; using Artemis.Core.Services; using Artemis.UI.Events; using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens.Home; using Artemis.UI.Screens.News; using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.Workshop; using Artemis.UI.Shared; using MaterialDesignExtensions.Controls; using MaterialDesignExtensions.Model; using MaterialDesignThemes.Wpf; using Ninject; using Stylet; namespace Artemis.UI.Screens.Sidebar { public sealed class SidebarViewModel : Screen, IHandle, IDisposable { private readonly Timer _activeModulesUpdateTimer; private readonly IKernel _kernel; private readonly IModuleVmFactory _moduleVmFactory; private readonly IPluginManagementService _pluginManagementService; private readonly IModuleService _moduleService; private string _activeModules; private bool _isSidebarOpen; private IScreen _selectedItem; private BindableCollection _sidebarItems; private Dictionary _sidebarModules; public SidebarViewModel(IKernel kernel, ISettingsService settingsService, IEventAggregator eventAggregator, IModuleVmFactory moduleVmFactory, IPluginManagementService pluginManagementService, IModuleService moduleService) { _kernel = kernel; _moduleVmFactory = moduleVmFactory; _pluginManagementService = pluginManagementService; _moduleService = moduleService; SidebarModules = new Dictionary(); SidebarItems = new BindableCollection(); PinSidebar = settingsService.GetSetting("UI.PinSidebar", false); PinSidebar.AutoSave = true; _activeModulesUpdateTimer = new Timer(1000); eventAggregator.Subscribe(this); } public PluginSetting PinSidebar { get; } public BindableCollection SidebarItems { get => _sidebarItems; set => SetAndNotify(ref _sidebarItems, value); } public Dictionary SidebarModules { get => _sidebarModules; set => SetAndNotify(ref _sidebarModules, value); } public string ActiveModules { get => _activeModules; set => SetAndNotify(ref _activeModules, value); } public IScreen SelectedItem { get => _selectedItem; set => SetAndNotify(ref _selectedItem, value); } public bool IsSidebarOpen { get => _isSidebarOpen; set { SetAndNotify(ref _isSidebarOpen, value); if (value) ActiveModulesUpdateTimerOnElapsed(this, EventArgs.Empty); } } public void SetupSidebar() { UpdateSidebarItems(); // Set the sidebar as open if it's pinned if (PinSidebar.Value) IsSidebarOpen = true; // Select the top item, which will be one of the defaults Task.Run(() => SelectSidebarItem(SidebarItems[0])); } private void UpdateSidebarItems() { SidebarItems.Clear(); SidebarModules.Clear(); // Add all default sidebar items SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Home, Label = "Home" }); SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Newspaper, Label = "News" }); SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.TestTube, Label = "Workshop" }); SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Edit, Label = "Surface Editor" }); SidebarItems.Add(new FirstLevelNavigationItem { Icon = PackIconKind.Settings, Label = "Settings" }); // Add all activated modules SidebarItems.Add(new DividerNavigationItem()); List modules = _pluginManagementService.GetFeaturesOfType().ToList(); foreach (IGrouping category in modules.OrderByDescending(m => m.PriorityCategory).GroupBy(m => m.PriorityCategory)) { SidebarItems.Add(new SubheaderNavigationItem { Subheader = category.Key.ToString() }); foreach(Module module in category.OrderBy(m => m.Priority)) { AddModule(module); } } } // ReSharper disable once UnusedMember.Global - Called by view public void SelectItem(WillSelectNavigationItemEventArgs args) { if (args.NavigationItemToSelect == null) { SelectedItem = null; return; } SelectSidebarItem(args.NavigationItemToSelect); } public void AddModule(Module module) { // Ensure the module is not already in the list if (SidebarModules.Any(io => io.Value == module)) return; FirstLevelNavigationItem sidebarItem = new() { Icon = PluginUtilities.GetPluginIcon(module.Plugin, module.DisplayIcon), Label = module.DisplayName }; SidebarItems.Add(sidebarItem); SidebarModules.Add(sidebarItem, module); } public void RemoveModule(Module module) { // If not in the list there's nothing to do if (SidebarModules.All(io => io.Value != module)) return; KeyValuePair existing = SidebarModules.First(io => io.Value == module); SidebarItems.Remove(existing.Key); SidebarModules.Remove(existing.Key); } private void ActiveModulesUpdateTimerOnElapsed(object sender, EventArgs e) { if (!IsSidebarOpen) return; int activeModules = SidebarModules.Count(m => m.Value.IsActivated); ActiveModules = activeModules == 1 ? "1 active module" : $"{activeModules} active modules"; } private void SelectSidebarItem(INavigationItem sidebarItem) { // A module was selected if the dictionary contains the selected item if (SidebarModules.ContainsKey(sidebarItem)) ActivateModule(sidebarItem); else if (sidebarItem is FirstLevelNavigationItem navigationItem) ActivateViewModel(navigationItem.Label); else SelectedItem = null; } private void ActivateViewModel(string label) { if (label == "Home") ActivateViewModel(); else if (label == "News") ActivateViewModel(); else if (label == "Workshop") ActivateViewModel(); else if (label == "Surface Editor") ActivateViewModel(); else if (label == "Settings") ActivateViewModel(); } private void ActivateViewModel() { SelectedItem = (IScreen) _kernel.Get(); } private void ActivateModule(INavigationItem sidebarItem) { SelectedItem = SidebarModules.ContainsKey(sidebarItem) ? _moduleVmFactory.CreateModuleRootViewModel(SidebarModules[sidebarItem]) : null; } #region IDisposable public void Dispose() { _activeModulesUpdateTimer?.Dispose(); } #endregion #region Event handlers private void OnFeatureEnabled(object sender, PluginFeatureEventArgs e) { if (e.PluginFeature is Module) UpdateSidebarItems(); } private void OnFeatureDisabled(object sender, PluginFeatureEventArgs e) { if (e.PluginFeature is Module) UpdateSidebarItems(); } private void OnModulePriorityUpdated(object sender, EventArgs e) { UpdateSidebarItems(); } public void Handle(RequestSelectSidebarItemEvent message) { ActivateViewModel(message.Label); } #endregion #region Overrides of Screen /// protected override void OnInitialActivate() { _activeModulesUpdateTimer.Start(); _activeModulesUpdateTimer.Elapsed += ActiveModulesUpdateTimerOnElapsed; _pluginManagementService.PluginFeatureEnabled += OnFeatureEnabled; _pluginManagementService.PluginFeatureDisabled += OnFeatureDisabled; _moduleService.ModulePriorityUpdated += OnModulePriorityUpdated; SetupSidebar(); base.OnInitialActivate(); } /// protected override void OnClose() { _activeModulesUpdateTimer.Stop(); _activeModulesUpdateTimer.Elapsed -= ActiveModulesUpdateTimerOnElapsed; _pluginManagementService.PluginFeatureEnabled -= OnFeatureEnabled; _pluginManagementService.PluginFeatureDisabled -= OnFeatureDisabled; _moduleService.ModulePriorityUpdated -= OnModulePriorityUpdated; base.OnClose(); } #endregion } }