1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Shared UI - Moved initialization code

Color picker - Added toggle to preview color on devices
Settings - Added toggable auto-save
This commit is contained in:
Robert 2020-09-24 21:33:56 +02:00
parent 33373bda57
commit fa329a9048
16 changed files with 276 additions and 103 deletions

View File

@ -48,12 +48,14 @@ namespace Artemis.Core
get => _value;
set
{
if (!Equals(_value, value))
{
_value = value;
OnSettingChanged();
NotifyOfPropertyChange(nameof(Value));
}
if (Equals(_value, value)) return;
_value = value;
OnSettingChanged();
NotifyOfPropertyChange(nameof(Value));
if (AutoSave)
Save();
}
}
@ -62,6 +64,12 @@ namespace Artemis.Core
/// </summary>
public bool HasChanged => JsonConvert.SerializeObject(Value) != _pluginSettingEntity.Value;
/// <summary>
/// Gets or sets whether changes must automatically be saved
/// <para>Note: When set to <c>true</c> <see cref="HasChanged" /> is always <c>false</c></para>
/// </summary>
public bool AutoSave { get; set; }
/// <summary>
/// Resets the setting to the last saved value
/// </summary>

View File

@ -0,0 +1,22 @@
using Artemis.UI.Shared.Services;
using Ninject;
namespace Artemis.UI.Shared
{
public static class Bootstrapper
{
public static bool Initialized { get; private set; }
public static void Initialize(IKernel kernel)
{
if (Initialized)
return;
var colorPickerService = kernel.Get<IColorPickerService>();
GradientPicker.ColorPickerService = colorPickerService;
ColorPicker.ColorPickerService = colorPickerService;
Initialized = true;
}
}
}

View File

@ -94,17 +94,18 @@
StaysOpen="{Binding StaysOpen, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
PopupAnimation="Fade"
IsOpen="{Binding PopupOpen, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
<materialDesign:Card Width="200" Height="200" Margin="30" materialDesign:ShadowAssist.ShadowDepth="Depth4">
<materialDesign:Card Width="200" Height="230" Margin="30" materialDesign:ShadowAssist.ShadowDepth="Depth4">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="160" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<materialDesign:ColorPicker Grid.Row="0"
Color="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
<materialDesign:ColorPicker Grid.Row="0"
Color="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
MouseUp="ColorGradient_OnMouseUp"
PreviewMouseDown="Slider_OnMouseDown"
PreviewMouseUp="Slider_OnMouseUp"/>
PreviewMouseUp="Slider_OnMouseUp" />
<Slider Grid.Row="1" Margin="8"
IsMoveToPointEnabled="True"
Orientation="Horizontal"
@ -114,6 +115,17 @@
PreviewMouseDown="Slider_OnMouseDown"
PreviewMouseUp="Slider_OnMouseUp"
Maximum="255" />
<!-- Checkboxes don't work inside popups inside tree views, which is where the color picker is regularly used -->
<shared:TreeViewPopupCompatibleCheckBox Grid.Row="2"
x:Name="PreviewCheckBox"
Margin="10 0"
VerticalAlignment="Center"
Style="{StaticResource MaterialDesignCheckBox}"
Click="PreviewCheckBoxClick">
Preview on devices
</shared:TreeViewPopupCompatibleCheckBox>
</Grid>
</materialDesign:Card>
</Popup>

View File

@ -5,6 +5,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Artemis.UI.Shared.Services;
namespace Artemis.UI.Shared
{
@ -13,6 +14,8 @@ namespace Artemis.UI.Shared
/// </summary>
public partial class ColorPicker : UserControl, INotifyPropertyChanged
{
private static IColorPickerService _colorPickerService;
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(nameof(Color), typeof(Color), typeof(ColorPicker),
new FrameworkPropertyMetadata(default(Color), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ColorPropertyChangedCallback));
@ -44,6 +47,21 @@ namespace Artemis.UI.Shared
public ColorPicker()
{
InitializeComponent();
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
/// <summary>
/// Used by the gradient picker to load saved gradients, do not touch or it'll just throw an exception
/// </summary>
internal static IColorPickerService ColorPickerService
{
set
{
if (_colorPickerService != null)
throw new AccessViolationException("This is not for you to touch");
_colorPickerService = value;
}
}
public Color Color
@ -87,6 +105,7 @@ namespace Artemis.UI.Shared
colorPicker.SetCurrentValue(ColorOpacityProperty, ((Color) e.NewValue).A);
colorPicker.OnPropertyChanged(nameof(Color));
_colorPickerService.UpdateColorDisplay(colorPicker.Color);
colorPicker._inCallback = false;
}
@ -126,6 +145,7 @@ namespace Artemis.UI.Shared
color = Color.FromArgb(opacity, color.R, color.G, color.B);
colorPicker.SetCurrentValue(ColorProperty, color);
colorPicker.OnPropertyChanged(nameof(ColorOpacity));
_colorPickerService.UpdateColorDisplay(colorPicker.Color);
colorPicker._inCallback = false;
}
@ -135,7 +155,7 @@ namespace Artemis.UI.Shared
PopupOpen = !PopupOpen;
e.Handled = true;
}
private void ColorGradient_OnMouseUp(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
@ -144,13 +164,39 @@ namespace Artemis.UI.Shared
private void Slider_OnMouseDown(object sender, MouseButtonEventArgs e)
{
OnDragStarted();
if (_colorPickerService.PreviewSetting.Value)
_colorPickerService.StartColorDisplay();
}
private void Slider_OnMouseUp(object sender, MouseButtonEventArgs e)
{
OnDragEnded();
_colorPickerService.StopColorDisplay();
}
private void PreviewCheckBoxClick(object sender, RoutedEventArgs e)
{
_colorPickerService.PreviewSetting.Value = PreviewCheckBox.IsChecked.Value;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
PreviewCheckBox.IsChecked = _colorPickerService.PreviewSetting.Value;
_colorPickerService.PreviewSetting.SettingChanged += PreviewSettingOnSettingChanged;
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
_colorPickerService.PreviewSetting.SettingChanged -= PreviewSettingOnSettingChanged;
}
private void PreviewSettingOnSettingChanged(object sender, EventArgs e)
{
PreviewCheckBox.IsChecked = _colorPickerService.PreviewSetting.Value;
}
#region Events
public event EventHandler DragStarted;
@ -167,6 +213,5 @@ namespace Artemis.UI.Shared
}
#endregion
}
}

View File

@ -16,7 +16,7 @@ namespace Artemis.UI.Shared
/// </summary>
public partial class GradientPicker : UserControl, INotifyPropertyChanged
{
private static IGradientPickerService _gradientPickerService;
private static IColorPickerService _colorPickerService;
private bool _inCallback;
public GradientPicker()
@ -27,14 +27,13 @@ namespace Artemis.UI.Shared
/// <summary>
/// Used by the gradient picker to load saved gradients, do not touch or it'll just throw an exception
/// </summary>
public static IGradientPickerService GradientPickerService
internal static IColorPickerService ColorPickerService
{
private get => _gradientPickerService;
set
{
if (_gradientPickerService != null)
if (_colorPickerService != null)
throw new AccessViolationException("This is not for you to touch");
_gradientPickerService = value;
_colorPickerService = value;
}
}
@ -93,7 +92,7 @@ namespace Artemis.UI.Shared
Execute.OnUIThread(async () =>
{
OnDialogOpened();
await GradientPickerService.ShowGradientPicker(ColorGradient, DialogHost);
await _colorPickerService.ShowGradientPicker(ColorGradient, DialogHost);
OnDialogClosed();
});
}

View File

@ -0,0 +1,22 @@
using System.Windows.Controls;
using System.Windows.Input;
namespace Artemis.UI.Shared
{
// Workaround for https://developercommunity.visualstudio.com/content/problem/190202/button-controls-hosted-in-popup-windows-do-not-wor.html
/// <inheritdoc />
public class TreeViewPopupCompatibleCheckBox : CheckBox
{
/// <inheritdoc />
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
}
/// <inheritdoc />
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
}
}
}

View File

@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Screens.GradientEditor;
using SkiaSharp;
namespace Artemis.UI.Shared.Services
{
internal class ColorPickerService : IColorPickerService
{
private readonly ICoreService _coreService;
private readonly IDialogService _dialogService;
private bool _mustRenderOverlay;
private SKColor _overlayColor;
private float _overlayOpacity;
public ColorPickerService(IDialogService dialogService, ICoreService coreService, ISettingsService settingsService)
{
_dialogService = dialogService;
_coreService = coreService;
PreviewSetting = settingsService.GetSetting("UI.PreviewColorPickerOnDevices", false);
PreviewSetting.AutoSave = true;
}
public PluginSetting<bool> PreviewSetting { get; }
public Task<object> ShowGradientPicker(ColorGradient colorGradient, string dialogHost)
{
if (!string.IsNullOrWhiteSpace(dialogHost))
return _dialogService.ShowDialogAt<GradientEditorViewModel>(dialogHost, new Dictionary<string, object> {{"colorGradient", colorGradient}});
return _dialogService.ShowDialog<GradientEditorViewModel>(new Dictionary<string, object> {{"colorGradient", colorGradient}});
}
public void StartColorDisplay()
{
if (_mustRenderOverlay)
return;
_overlayOpacity = 0f;
_mustRenderOverlay = true;
_coreService.FrameRendering += RenderColorPickerOverlay;
}
public void StopColorDisplay()
{
_mustRenderOverlay = false;
}
public void UpdateColorDisplay(Color color)
{
_overlayColor = new SKColor(color.R, color.G, color.B, color.A);
}
private void RenderColorPickerOverlay(object sender, FrameRenderingEventArgs e)
{
if (_mustRenderOverlay)
_overlayOpacity += 0.2f;
else
_overlayOpacity -= 0.2f;
if (_overlayOpacity <= 0f)
{
_coreService.FrameRendering -= RenderColorPickerOverlay;
return;
}
if (_overlayOpacity > 1f)
_overlayOpacity = 1f;
using var overlayPaint = new SKPaint {Color = new SKColor(0, 0, 0, (byte) (255 * _overlayOpacity))};
overlayPaint.Color = _overlayColor.WithAlpha((byte) (_overlayColor.Alpha * _overlayOpacity));
e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint);
}
}
}

View File

@ -1,24 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Shared.Screens.GradientEditor;
namespace Artemis.UI.Shared.Services
{
internal class GradientPickerService : IGradientPickerService
{
private readonly IDialogService _dialogService;
public GradientPickerService(IDialogService dialogService)
{
_dialogService = dialogService;
}
public Task<object> ShowGradientPicker(ColorGradient colorGradient, string dialogHost)
{
if (!string.IsNullOrWhiteSpace(dialogHost))
return _dialogService.ShowDialogAt<GradientEditorViewModel>(dialogHost, new Dictionary<string, object> {{"colorGradient", colorGradient}});
return _dialogService.ShowDialog<GradientEditorViewModel>(new Dictionary<string, object> {{"colorGradient", colorGradient}});
}
}
}

View File

@ -0,0 +1,16 @@
using System.Threading.Tasks;
using System.Windows.Media;
using Artemis.Core;
namespace Artemis.UI.Shared.Services
{
internal interface IColorPickerService : IArtemisSharedUIService
{
Task<object> ShowGradientPicker(ColorGradient colorGradient, string dialogHost);
PluginSetting<bool> PreviewSetting { get; }
void StartColorDisplay();
void StopColorDisplay();
void UpdateColorDisplay(Color color);
}
}

View File

@ -1,10 +0,0 @@
using System.Threading.Tasks;
using Artemis.Core;
namespace Artemis.UI.Shared.Services
{
public interface IGradientPickerService : IArtemisSharedUIService
{
Task<object> ShowGradientPicker(ColorGradient colorGradient, string dialogHost);
}
}

View File

@ -30,21 +30,22 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
public ConditionalDataBinding<TLayerProperty, TProperty> ConditionalDataBinding { get; }
public BindableCollection<DataBindingConditionViewModel<TLayerProperty, TProperty>> ConditionViewModels { get; }
public bool SupportsTestValue => false;
public void Update()
{
UpdateConditionViewModels();
}
public object GetTestValue()
{
return ConditionalDataBinding.DataBinding.Converter.GetValue();
throw new NotSupportedException();
}
public void AddCondition(string type)
{
var condition = ConditionalDataBinding.AddCondition();
// Find the VM of the new condition
var viewModel = ConditionViewModels.First(c => c.DataBindingCondition == condition);
viewModel.ActiveItem.AddCondition(type);
@ -58,7 +59,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
// Remove old VMs
var toRemove = ConditionViewModels.Where(c => !ConditionalDataBinding.Conditions.Contains(c.DataBindingCondition)).ToList();
foreach (var dataBindingConditionViewModel in toRemove) {
foreach (var dataBindingConditionViewModel in toRemove)
{
ConditionViewModels.Remove(dataBindingConditionViewModel);
dataBindingConditionViewModel.Dispose();
}
@ -72,7 +74,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
// Fix order
ConditionViewModels.Sort(c => c.DataBindingCondition.Order);
_updating = false;
}

View File

@ -21,13 +21,13 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<materialDesign:Card Grid.Column="0"
UniformCornerRadius="0"
materialDesign:ShadowAssist.ShadowDepth="Depth3"
materialDesign:ShadowAssist.ShadowEdges="Right"
Background="{DynamicResource MaterialDesignPaper}"
Panel.ZIndex="2">
<Grid Margin="10 5" >
<materialDesign:Card Grid.Column="0"
UniformCornerRadius="0"
materialDesign:ShadowAssist.ShadowDepth="Depth3"
materialDesign:ShadowAssist.ShadowEdges="Right"
Background="{DynamicResource MaterialDesignPaper}"
Panel.ZIndex="2">
<Grid Margin="10 5">
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="48" />
@ -39,11 +39,10 @@
Style="{StaticResource MaterialDesignFloatingHintComboBox}"
materialDesign:HintAssist.Hint="Data binding mode"
MinWidth="128"
SelectedValue="{Binding SelectedDataBindingMode}"
ItemsSource="{Binding DataBindingModes}"
SelectedValue="{Binding SelectedDataBindingMode}"
ItemsSource="{Binding DataBindingModes}"
SelectedValuePath="Value"
DisplayMemberPath="Description" >
</ComboBox>
DisplayMemberPath="Description" />
<StackPanel Grid.Row="1">
<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}"
@ -102,23 +101,24 @@
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Margin="0 2">Input</TextBlock>
<TextBlock Margin="0 2" Visibility="{Binding ActiveItem.SupportsTestValue, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">Input</TextBlock>
<ContentControl Grid.Row="0"
Grid.Column="1"
s:View.Model="{Binding TestInputValue}"
Margin="0 2"
FontFamily="Consolas"
VerticalAlignment="Stretch"
HorizontalAlignment="Right" />
Grid.Column="1"
Visibility="{Binding ActiveItem.SupportsTestValue, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
s:View.Model="{Binding TestInputValue}"
Margin="0 2"
FontFamily="Consolas"
VerticalAlignment="Stretch"
HorizontalAlignment="Right" />
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0 2">Output</TextBlock>
<ContentControl Grid.Row="1"
Grid.Column="1"
s:View.Model="{Binding TestResultValue}"
Margin="0 2"
FontFamily="Consolas"
VerticalAlignment="Stretch"
HorizontalAlignment="Right" />
<ContentControl Grid.Row="1"
Grid.Column="1"
s:View.Model="{Binding TestResultValue}"
Margin="0 2"
FontFamily="Consolas"
VerticalAlignment="Stretch"
HorizontalAlignment="Right" />
</Grid>
</Grid>
</materialDesign:Card>
@ -127,10 +127,10 @@
<materialDesign:Card Grid.Column="1" UniformCornerRadius="0" Background="{DynamicResource MaterialDesignToolBarBackground}" Panel.ZIndex="1">
<Grid Margin="10 5">
<ContentControl s:View.Model="{Binding ActiveItem, IsAsync=True}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False"/>
<ContentControl s:View.Model="{Binding ActiveItem, IsAsync=True}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False" />
</Grid>
</materialDesign:Card>
</Grid>

View File

@ -205,17 +205,23 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private void UpdateTestResult()
{
if (Registration.DataBinding == null)
if (Registration.DataBinding == null || ActiveItem == null)
{
TestInputValue.UpdateValue(default);
TestResultValue.UpdateValue(default);
return;
}
var currentValue = Registration.Converter.ConvertFromObject(ActiveItem?.GetTestValue() ?? default(TProperty));
TestInputValue.UpdateValue(currentValue);
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default);
if (ActiveItem.SupportsTestValue)
{
var currentValue = Registration.Converter.ConvertFromObject(ActiveItem?.GetTestValue() ?? default(TProperty));
TestInputValue.UpdateValue(currentValue);
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default);
}
else
{
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(default) : default);
}
}
private void EnableDataBinding()

View File

@ -38,6 +38,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
public DirectDataBinding<TLayerProperty, TProperty> DirectDataBinding { get; }
public BindableCollection<DataBindingModifierViewModel<TLayerProperty, TProperty>> ModifierViewModels { get; }
public bool SupportsTestValue => true;
public bool CanAddModifier
{

View File

@ -6,6 +6,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
public interface IDataBindingModeViewModel : IScreen, IDisposable
{
void Update();
bool SupportsTestValue { get; }
object GetTestValue();
}
}

View File

@ -3,8 +3,6 @@ using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Screens.Splash;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Ninject;
using Stylet;
@ -12,15 +10,14 @@ namespace Artemis.UI.Screens
{
public class TrayViewModel : Screen
{
private readonly IDebugService _debugService;
private readonly IEventAggregator _eventAggregator;
private readonly IKernel _kernel;
private readonly IWindowManager _windowManager;
private readonly IDebugService _debugService;
private bool _canShowRootViewModel;
private bool _setGradientPickerService;
private SplashViewModel _splashViewModel;
public TrayViewModel(IKernel kernel, IWindowManager windowManager, IEventAggregator eventAggregator, ICoreService coreService, IDebugService debugService,ISettingsService settingsService)
public TrayViewModel(IKernel kernel, IWindowManager windowManager, IEventAggregator eventAggregator, ICoreService coreService, IDebugService debugService, ISettingsService settingsService)
{
_kernel = kernel;
_windowManager = windowManager;
@ -48,13 +45,9 @@ namespace Artemis.UI.Screens
if (!CanShowRootViewModel)
return;
// The gradient picker must have a reference to this service to be able to load saved gradients.
// To avoid wasting resources, only set the service once and not until showing the UI.
if (!_setGradientPickerService)
{
GradientPicker.GradientPickerService = _kernel.Get<IGradientPickerService>();
_setGradientPickerService = true;
}
// Initialize the shared UI when first showing the window
if (!UI.Shared.Bootstrapper.Initialized)
UI.Shared.Bootstrapper.Initialize(_kernel);
CanShowRootViewModel = false;
Execute.OnUIThread(() =>