diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 731969af2..ff30fffb9 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -67,35 +67,38 @@ namespace Artemis.Core.Models.Profile if (RenderPath == null) return; - canvas.Save(); + // TODO Just lock the whole thing, this is asking for deadlock lock (_renderBitmap) { - foreach (var layerElement in LayerElements) - layerElement.RenderPreProcess(surface, canvas); - - _renderCanvas.Clear(); - foreach (var layerElement in LayerElements) - layerElement.Render(surface, _renderCanvas); - - var baseShader = SKShader.CreateBitmap(_renderBitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, SKMatrix.MakeTranslation(RenderRectangle.Left, RenderRectangle.Top)); - foreach (var layerElement in LayerElements) + lock (LayerElements) { - var newBaseShader = layerElement.RenderPostProcess(surface, _renderBitmap, baseShader); - if (newBaseShader == null) - continue; + canvas.Save(); + foreach (var layerElement in LayerElements) + layerElement.RenderPreProcess(surface, canvas); - // Dispose the old base shader if the layer element provided a new one - if (!ReferenceEquals(baseShader, newBaseShader)) - baseShader.Dispose(); + _renderCanvas.Clear(); + foreach (var layerElement in LayerElements) + layerElement.Render(surface, _renderCanvas); - baseShader = newBaseShader; + var baseShader = SKShader.CreateBitmap(_renderBitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, SKMatrix.MakeTranslation(RenderRectangle.Left, RenderRectangle.Top)); + foreach (var layerElement in LayerElements) + { + var newBaseShader = layerElement.RenderPostProcess(surface, _renderBitmap, baseShader); + if (newBaseShader == null) + continue; + + // Dispose the old base shader if the layer element provided a new one + if (!ReferenceEquals(baseShader, newBaseShader)) + baseShader.Dispose(); + + baseShader = newBaseShader; + } + + canvas.ClipPath(RenderPath); + canvas.DrawRect(RenderRectangle, new SKPaint {Shader = baseShader, FilterQuality = SKFilterQuality.Low}); + baseShader.Dispose(); + canvas.Restore(); } - - canvas.ClipPath(RenderPath); - canvas.DrawRect(RenderRectangle, new SKPaint {Shader = baseShader, FilterQuality = SKFilterQuality.Low}); - baseShader.Dispose(); - - canvas.Restore(); } } @@ -162,12 +165,18 @@ namespace Artemis.Core.Models.Profile internal void AddLayerElement(LayerElement layerElement) { - _layerElements.Add(layerElement); + lock (LayerElements) + { + _layerElements.Add(layerElement); + } } internal void RemoveLayerElement(LayerElement layerElement) { - _layerElements.Remove(layerElement); + lock (LayerElements) + { + _layerElements.Remove(layerElement); + } } internal void PopulateLeds(ArtemisSurface surface) diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 7a85598bb..e59d187ea 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -77,7 +77,8 @@ namespace Artemis.Core.Services.Storage public void ActivateProfile(ProfileModule module, Profile profile) { module.ChangeActiveProfile(profile, _surfaceService.ActiveSurface); - InstantiateProfileLayerElements(profile); + if (profile != null) + InstantiateProfileLayerElements(profile); } public void DeleteProfile(Profile profile) diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 94e5414a0..b40eb1fab 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -139,6 +139,7 @@ + ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll @@ -170,6 +171,9 @@ Designer + + ColorPickerView.xaml + @@ -177,11 +181,13 @@ + + @@ -247,6 +253,10 @@ Code + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/src/Artemis.UI/Controls/ColorPicker/ColorPickerView.xaml b/src/Artemis.UI/Controls/ColorPicker/ColorPickerView.xaml new file mode 100644 index 000000000..88b805a2e --- /dev/null +++ b/src/Artemis.UI/Controls/ColorPicker/ColorPickerView.xaml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Controls/ColorPicker/ColorPickerView.xaml.cs b/src/Artemis.UI/Controls/ColorPicker/ColorPickerView.xaml.cs new file mode 100644 index 000000000..2bb475ecf --- /dev/null +++ b/src/Artemis.UI/Controls/ColorPicker/ColorPickerView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Artemis.UI.Controls.ColorPicker +{ + /// + /// Interaction logic for ColorPickerView.xaml + /// + public partial class ColorPickerView : UserControl + { + public ColorPickerView() + { + InitializeComponent(); + } + } +} diff --git a/src/Artemis.UI/Events/WindowsThemeEventArgs.cs b/src/Artemis.UI/Events/WindowsThemeEventArgs.cs new file mode 100644 index 000000000..54310f545 --- /dev/null +++ b/src/Artemis.UI/Events/WindowsThemeEventArgs.cs @@ -0,0 +1,15 @@ +using System; +using Artemis.UI.Utilities; + +namespace Artemis.UI.Events +{ + public class WindowsThemeEventArgs : EventArgs + { + public WindowsThemeEventArgs(ThemeWatcher.WindowsTheme theme) + { + Theme = theme; + } + + public ThemeWatcher.WindowsTheme Theme { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileDeviceView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileDeviceView.xaml index af09e06c0..5fda64a2a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileDeviceView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileDeviceView.xaml @@ -6,6 +6,7 @@ xmlns:s="https://github.com/canton7/Stylet" xmlns:converters="clr-namespace:Artemis.UI.Converters" xmlns:visualization="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.Visualization" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" mc:Ignorable="d" d:DataContext="{d:DesignInstance {x:Type visualization:ProfileDeviceViewModel}}" d:DesignHeight="450" d:DesignWidth="800"> @@ -20,7 +21,9 @@ - + + + - + @@ -60,10 +60,10 @@ - + - - + + diff --git a/src/Artemis.UI/Screens/RootView.xaml b/src/Artemis.UI/Screens/RootView.xaml index bc2b0ca91..7a7f20721 100644 --- a/src/Artemis.UI/Screens/RootView.xaml +++ b/src/Artemis.UI/Screens/RootView.xaml @@ -12,7 +12,11 @@ Icon="/Artemis.UI;component/Resources/logo-512.png" Title="Artemis" GlowBrush="{DynamicResource AccentColorBrush}" - FontFamily="{StaticResource DefaultFont}" + TextElement.Foreground="{DynamicResource MaterialDesignBody}" + Background="{DynamicResource MaterialDesignPaper}" + TextElement.FontWeight="Medium" + TextElement.FontSize="14" + FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto" SaveWindowPosition="True" UseLayoutRounding="True" Deactivated="{s:Action WindowDeactivated}" diff --git a/src/Artemis.UI/Screens/RootViewModel.cs b/src/Artemis.UI/Screens/RootViewModel.cs index 368d4aa9a..bb3f6d05a 100644 --- a/src/Artemis.UI/Screens/RootViewModel.cs +++ b/src/Artemis.UI/Screens/RootViewModel.cs @@ -13,6 +13,8 @@ using Artemis.UI.Screens.News; using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.Workshop; +using Artemis.UI.Utilities; +using MaterialDesignThemes.Wpf; using Stylet; namespace Artemis.UI.Screens @@ -47,6 +49,19 @@ namespace Artemis.UI.Screens _pluginService.PluginDisabled += PluginServiceOnPluginDisabled; PropertyChanged += OnSelectedModuleChanged; PropertyChanged += OnSelectedPageChanged; + + var themeWatcher = new ThemeWatcher(); + themeWatcher.ThemeChanged += (sender, args) => ApplyWindowsTheme(args.Theme); + ApplyWindowsTheme(themeWatcher.GetWindowsTheme()); + } + + private void ApplyWindowsTheme(ThemeWatcher.WindowsTheme windowsTheme) + { + var paletteHelper = new PaletteHelper(); + var theme = paletteHelper.GetTheme(); + + theme.SetBaseTheme(windowsTheme == ThemeWatcher.WindowsTheme.Dark ? Theme.Dark : Theme.Light); + paletteHelper.SetTheme(theme); } public IObservableCollection Modules { get; set; } diff --git a/src/Artemis.UI/Screens/Workshop/WorkshopView.xaml b/src/Artemis.UI/Screens/Workshop/WorkshopView.xaml index 5a6d3dbdf..076ee0c1a 100644 --- a/src/Artemis.UI/Screens/Workshop/WorkshopView.xaml +++ b/src/Artemis.UI/Screens/Workshop/WorkshopView.xaml @@ -8,6 +8,6 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - Work work! + Work work! \ No newline at end of file diff --git a/src/Artemis.UI/Utilities/ThemeWatcher.cs b/src/Artemis.UI/Utilities/ThemeWatcher.cs new file mode 100644 index 000000000..fbf7db504 --- /dev/null +++ b/src/Artemis.UI/Utilities/ThemeWatcher.cs @@ -0,0 +1,76 @@ +using System; +using System.Globalization; +using System.Management; +using System.Security.Principal; +using Artemis.UI.Events; +using Microsoft.Win32; + +namespace Artemis.UI.Utilities +{ + public class ThemeWatcher + { + public ThemeWatcher() + { + WatchTheme(); + } + + public enum WindowsTheme + { + Light, + Dark + } + + private const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; + + private const string RegistryValueName = "AppsUseLightTheme"; + + public void WatchTheme() + { + var currentUser = WindowsIdentity.GetCurrent(); + var query = string.Format( + CultureInfo.InvariantCulture, + @"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'", + currentUser.User.Value, + RegistryKeyPath.Replace(@"\", @"\\"), + RegistryValueName); + + try + { + var watcher = new ManagementEventWatcher(query); + watcher.EventArrived += (sender, args) => + { + var newWindowsTheme = GetWindowsTheme(); + OnThemeChanged(new WindowsThemeEventArgs(newWindowsTheme)); + }; + + // Start listening for events + watcher.Start(); + } + catch (Exception) + { + // This can fail on Windows 7 + } + } + + public WindowsTheme GetWindowsTheme() + { + using (var key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath)) + { + var registryValueObject = key?.GetValue(RegistryValueName); + if (registryValueObject == null) return WindowsTheme.Light; + + var registryValue = (int) registryValueObject; + + return registryValue > 0 ? WindowsTheme.Light : WindowsTheme.Dark; + } + } + + public event EventHandler ThemeChanged; + + + protected virtual void OnThemeChanged(WindowsThemeEventArgs e) + { + ThemeChanged?.Invoke(this, e); + } + } +} \ No newline at end of file