607 lines
24 KiB
C#
607 lines
24 KiB
C#
|
|
using MainShell.Resources.CustomControl;
|
|||
|
|
using Newtonsoft.Json;
|
|||
|
|
using Stylet;
|
|||
|
|
using System;
|
|||
|
|
using System.Globalization;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Windows;
|
|||
|
|
using System.Windows.Controls;
|
|||
|
|
using System.Windows.Controls.Primitives;
|
|||
|
|
using System.Windows.Data;
|
|||
|
|
using System.Windows.Input;
|
|||
|
|
using System.Windows.Media;
|
|||
|
|
using System.Windows.Threading;
|
|||
|
|
|
|||
|
|
namespace MainShell.Common
|
|||
|
|
{
|
|||
|
|
public static class HeaderDeviceStatusOverlayHost
|
|||
|
|
{
|
|||
|
|
private static readonly object OverlayRootTag = new object();
|
|||
|
|
private static readonly HeaderDeviceStatusOverlaySettings OverlaySettings = new HeaderDeviceStatusOverlaySettings();
|
|||
|
|
|
|||
|
|
public static void Attach(Window window, HeaderDeviceStatusController controller)
|
|||
|
|
{
|
|||
|
|
if (window == null)
|
|||
|
|
{
|
|||
|
|
throw new ArgumentNullException(nameof(window));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (controller == null)
|
|||
|
|
{
|
|||
|
|
throw new ArgumentNullException(nameof(controller));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
window.Dispatcher.BeginInvoke(
|
|||
|
|
DispatcherPriority.Loaded,
|
|||
|
|
new Action(() => AttachCore(window, controller)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void AttachCore(Window window, HeaderDeviceStatusController controller)
|
|||
|
|
{
|
|||
|
|
Grid existingOverlayRoot = window.Content as Grid;
|
|||
|
|
if (existingOverlayRoot != null && ReferenceEquals(existingOverlayRoot.Tag, OverlayRootTag))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UIElement currentContent = window.Content as UIElement;
|
|||
|
|
if (currentContent == null)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Grid overlayRoot = new Grid();
|
|||
|
|
overlayRoot.Tag = OverlayRootTag;
|
|||
|
|
|
|||
|
|
window.Content = null;
|
|||
|
|
overlayRoot.Children.Add(currentContent);
|
|||
|
|
overlayRoot.Children.Add(CreateOverlayElement(controller));
|
|||
|
|
overlayRoot.Children.Add(CreateDebugPanelElement());
|
|||
|
|
window.Content = overlayRoot;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static FrameworkElement CreateOverlayElement(HeaderDeviceStatusController controller)
|
|||
|
|
{
|
|||
|
|
HeaderDeviceStatusView statusView = new HeaderDeviceStatusView();
|
|||
|
|
statusView.DataContext = controller;
|
|||
|
|
statusView.IsHitTestVisible = false;
|
|||
|
|
statusView.HorizontalAlignment = HorizontalAlignment.Left;
|
|||
|
|
statusView.VerticalAlignment = VerticalAlignment.Top;
|
|||
|
|
|
|||
|
|
Binding marginBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.Margin));
|
|||
|
|
marginBinding.Source = OverlaySettings;
|
|||
|
|
BindingOperations.SetBinding(statusView, FrameworkElement.MarginProperty, marginBinding);
|
|||
|
|
|
|||
|
|
Panel.SetZIndex(statusView, short.MaxValue);
|
|||
|
|
return statusView;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static FrameworkElement CreateDebugPanelElement()
|
|||
|
|
{
|
|||
|
|
Border panelBorder = new Border();
|
|||
|
|
panelBorder.Background = new SolidColorBrush(Color.FromArgb(235, 18, 26, 35));
|
|||
|
|
panelBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(72, 93, 118));
|
|||
|
|
panelBorder.BorderThickness = new Thickness(1.0);
|
|||
|
|
panelBorder.CornerRadius = new CornerRadius(6.0);
|
|||
|
|
panelBorder.Padding = new Thickness(10.0, 8.0, 10.0, 8.0);
|
|||
|
|
panelBorder.HorizontalAlignment = HorizontalAlignment.Left;
|
|||
|
|
panelBorder.VerticalAlignment = VerticalAlignment.Top;
|
|||
|
|
|
|||
|
|
Binding panelMarginBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.DebugPanelMargin));
|
|||
|
|
panelMarginBinding.Source = OverlaySettings;
|
|||
|
|
BindingOperations.SetBinding(panelBorder, FrameworkElement.MarginProperty, panelMarginBinding);
|
|||
|
|
|
|||
|
|
Binding panelVisibilityBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.IsDebugPanelVisible));
|
|||
|
|
panelVisibilityBinding.Source = OverlaySettings;
|
|||
|
|
panelVisibilityBinding.Converter = new BooleanToVisibilityConverter();
|
|||
|
|
BindingOperations.SetBinding(panelBorder, UIElement.VisibilityProperty, panelVisibilityBinding);
|
|||
|
|
|
|||
|
|
StackPanel rootPanel = new StackPanel();
|
|||
|
|
rootPanel.Orientation = Orientation.Vertical;
|
|||
|
|
|
|||
|
|
DockPanel headerPanel = new DockPanel();
|
|||
|
|
|
|||
|
|
TextBlock titleText = new TextBlock();
|
|||
|
|
titleText.Text = "Overlay Debug";
|
|||
|
|
titleText.Foreground = Brushes.White;
|
|||
|
|
titleText.FontSize = 13.0;
|
|||
|
|
titleText.FontWeight = FontWeights.SemiBold;
|
|||
|
|
titleText.Margin = new Thickness(0.0, 0.0, 8.0, 6.0);
|
|||
|
|
DockPanel.SetDock(titleText, Dock.Left);
|
|||
|
|
headerPanel.Children.Add(titleText);
|
|||
|
|
|
|||
|
|
Button closeButton = CreateDebugButton("<22><><EFBFBD><EFBFBD>", 44.0);
|
|||
|
|
closeButton.Margin = new Thickness(6.0, 0.0, 0.0, 6.0);
|
|||
|
|
closeButton.Click += delegate(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
OverlaySettings.HideDebugPanel();
|
|||
|
|
};
|
|||
|
|
DockPanel.SetDock(closeButton, Dock.Right);
|
|||
|
|
headerPanel.Children.Add(closeButton);
|
|||
|
|
|
|||
|
|
rootPanel.Children.Add(headerPanel);
|
|||
|
|
|
|||
|
|
TextBlock summaryText = new TextBlock();
|
|||
|
|
summaryText.Foreground = new SolidColorBrush(Color.FromRgb(210, 221, 236));
|
|||
|
|
summaryText.FontSize = 12.0;
|
|||
|
|
summaryText.Margin = new Thickness(0.0, 0.0, 0.0, 8.0);
|
|||
|
|
Binding summaryBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.PositionSummary));
|
|||
|
|
summaryBinding.Source = OverlaySettings;
|
|||
|
|
BindingOperations.SetBinding(summaryText, TextBlock.TextProperty, summaryBinding);
|
|||
|
|
rootPanel.Children.Add(summaryText);
|
|||
|
|
|
|||
|
|
rootPanel.Children.Add(CreateDebugInputRow("Left", nameof(HeaderDeviceStatusOverlaySettings.LeftMarginText)));
|
|||
|
|
rootPanel.Children.Add(CreateDebugInputRow("Top", nameof(HeaderDeviceStatusOverlaySettings.TopMarginText)));
|
|||
|
|
rootPanel.Children.Add(CreateDebugInputRow("Step", nameof(HeaderDeviceStatusOverlaySettings.StepText)));
|
|||
|
|
|
|||
|
|
UniformGrid actionGrid = new UniformGrid();
|
|||
|
|
actionGrid.Columns = 4;
|
|||
|
|
actionGrid.Rows = 1;
|
|||
|
|
actionGrid.Margin = new Thickness(0.0, 0.0, 0.0, 8.0);
|
|||
|
|
|
|||
|
|
Button leftButton = CreateDebugButton("<22><>", 40.0);
|
|||
|
|
leftButton.Click += delegate(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
OverlaySettings.AdjustMargin(-OverlaySettings.Step, 0.0);
|
|||
|
|
};
|
|||
|
|
actionGrid.Children.Add(leftButton);
|
|||
|
|
|
|||
|
|
Button rightButton = CreateDebugButton("<22><>", 40.0);
|
|||
|
|
rightButton.Click += delegate(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
OverlaySettings.AdjustMargin(OverlaySettings.Step, 0.0);
|
|||
|
|
};
|
|||
|
|
actionGrid.Children.Add(rightButton);
|
|||
|
|
|
|||
|
|
Button upButton = CreateDebugButton("<22><>", 40.0);
|
|||
|
|
upButton.Click += delegate(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
OverlaySettings.AdjustMargin(0.0, -OverlaySettings.Step);
|
|||
|
|
};
|
|||
|
|
actionGrid.Children.Add(upButton);
|
|||
|
|
|
|||
|
|
Button downButton = CreateDebugButton("<22><>", 40.0);
|
|||
|
|
downButton.Click += delegate(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
OverlaySettings.AdjustMargin(0.0, OverlaySettings.Step);
|
|||
|
|
};
|
|||
|
|
actionGrid.Children.Add(downButton);
|
|||
|
|
|
|||
|
|
rootPanel.Children.Add(actionGrid);
|
|||
|
|
|
|||
|
|
Button applyButton = CreateDebugButton("Ӧ<><D3A6>", 0.0);
|
|||
|
|
applyButton.Width = 178.0;
|
|||
|
|
applyButton.Margin = new Thickness(0.0, 0.0, 0.0, 0.0);
|
|||
|
|
applyButton.Click += delegate(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
OverlaySettings.ApplyTextValues();
|
|||
|
|
};
|
|||
|
|
rootPanel.Children.Add(applyButton);
|
|||
|
|
|
|||
|
|
panelBorder.Child = rootPanel;
|
|||
|
|
Panel.SetZIndex(panelBorder, short.MaxValue);
|
|||
|
|
return panelBorder;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static FrameworkElement CreateDebugInputRow(string labelText, string bindingPath)
|
|||
|
|
{
|
|||
|
|
Grid rowGrid = new Grid();
|
|||
|
|
rowGrid.Margin = new Thickness(0.0, 0.0, 0.0, 6.0);
|
|||
|
|
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(44.0) });
|
|||
|
|
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(92.0) });
|
|||
|
|
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(56.0) });
|
|||
|
|
|
|||
|
|
TextBlock label = new TextBlock();
|
|||
|
|
label.Text = labelText;
|
|||
|
|
label.Foreground = new SolidColorBrush(Color.FromRgb(210, 221, 236));
|
|||
|
|
label.FontSize = 12.0;
|
|||
|
|
label.VerticalAlignment = VerticalAlignment.Center;
|
|||
|
|
Grid.SetColumn(label, 0);
|
|||
|
|
rowGrid.Children.Add(label);
|
|||
|
|
|
|||
|
|
TextBox inputBox = CreateDebugTextBox(bindingPath);
|
|||
|
|
Grid.SetColumn(inputBox, 1);
|
|||
|
|
rowGrid.Children.Add(inputBox);
|
|||
|
|
|
|||
|
|
TextBlock hint = new TextBlock();
|
|||
|
|
hint.Text = "<22>س<EFBFBD>";
|
|||
|
|
hint.Foreground = new SolidColorBrush(Color.FromRgb(150, 167, 188));
|
|||
|
|
hint.FontSize = 11.0;
|
|||
|
|
hint.VerticalAlignment = VerticalAlignment.Center;
|
|||
|
|
hint.HorizontalAlignment = HorizontalAlignment.Right;
|
|||
|
|
Grid.SetColumn(hint, 2);
|
|||
|
|
rowGrid.Children.Add(hint);
|
|||
|
|
|
|||
|
|
return rowGrid;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static TextBox CreateDebugTextBox(string bindingPath)
|
|||
|
|
{
|
|||
|
|
TextBox inputBox = new TextBox();
|
|||
|
|
inputBox.Height = 26.0;
|
|||
|
|
inputBox.Margin = new Thickness(0.0);
|
|||
|
|
inputBox.Padding = new Thickness(6.0, 2.0, 6.0, 2.0);
|
|||
|
|
inputBox.Background = new SolidColorBrush(Color.FromRgb(33, 46, 60));
|
|||
|
|
inputBox.BorderBrush = new SolidColorBrush(Color.FromRgb(96, 122, 149));
|
|||
|
|
inputBox.Foreground = Brushes.White;
|
|||
|
|
inputBox.FontSize = 12.0;
|
|||
|
|
inputBox.VerticalContentAlignment = VerticalAlignment.Center;
|
|||
|
|
|
|||
|
|
Binding textBinding = new Binding(bindingPath);
|
|||
|
|
textBinding.Source = OverlaySettings;
|
|||
|
|
textBinding.Mode = BindingMode.TwoWay;
|
|||
|
|
textBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
|
|||
|
|
BindingOperations.SetBinding(inputBox, TextBox.TextProperty, textBinding);
|
|||
|
|
|
|||
|
|
inputBox.LostFocus += delegate(object sender, RoutedEventArgs e)
|
|||
|
|
{
|
|||
|
|
OverlaySettings.ApplyTextValues();
|
|||
|
|
};
|
|||
|
|
inputBox.KeyDown += delegate(object sender, KeyEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (e.Key != Key.Enter)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
OverlaySettings.ApplyTextValues();
|
|||
|
|
e.Handled = true;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return inputBox;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static Button CreateDebugButton(string content, double width)
|
|||
|
|
{
|
|||
|
|
Button button = new Button();
|
|||
|
|
button.Content = content;
|
|||
|
|
if (width > 0.0)
|
|||
|
|
{
|
|||
|
|
button.Width = width;
|
|||
|
|
}
|
|||
|
|
button.Height = 28.0;
|
|||
|
|
button.Margin = new Thickness(0.0, 0.0, 6.0, 0.0);
|
|||
|
|
button.Padding = new Thickness(4.0, 0.0, 4.0, 0.0);
|
|||
|
|
button.Background = new SolidColorBrush(Color.FromRgb(43, 62, 81));
|
|||
|
|
button.BorderBrush = new SolidColorBrush(Color.FromRgb(96, 122, 149));
|
|||
|
|
button.Foreground = Brushes.White;
|
|||
|
|
button.FontSize = 12.0;
|
|||
|
|
return button;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private sealed class HeaderDeviceStatusOverlaySettings : PropertyChangedBase, IDisposable
|
|||
|
|
{
|
|||
|
|
private const double DefaultTopMargin = 8.0;
|
|||
|
|
private const double DefaultLeftMargin = 8.0;
|
|||
|
|
private const double DefaultDebugPanelTopOffset = 50.0;
|
|||
|
|
private const double DefaultAdjustStep = 1.0;
|
|||
|
|
private const string ConfigurationDirectoryName = "Configuration";
|
|||
|
|
private const string ConfigurationFileName = "HeaderDeviceStatusOverlay.json";
|
|||
|
|
|
|||
|
|
private readonly string _configurationDirectoryPath;
|
|||
|
|
private readonly string _configurationFilePath;
|
|||
|
|
private readonly FileSystemWatcher _fileWatcher;
|
|||
|
|
private double _topMargin = DefaultTopMargin;
|
|||
|
|
private double _leftMargin = DefaultLeftMargin;
|
|||
|
|
private bool _isDebugPanelVisible;
|
|||
|
|
private double _step = DefaultAdjustStep;
|
|||
|
|
private string _leftMarginText = DefaultLeftMargin.ToString("0.##", CultureInfo.InvariantCulture);
|
|||
|
|
private string _topMarginText = DefaultTopMargin.ToString("0.##", CultureInfo.InvariantCulture);
|
|||
|
|
private string _stepText = DefaultAdjustStep.ToString("0.##", CultureInfo.InvariantCulture);
|
|||
|
|
|
|||
|
|
public HeaderDeviceStatusOverlaySettings()
|
|||
|
|
{
|
|||
|
|
_configurationDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationDirectoryName);
|
|||
|
|
_configurationFilePath = Path.Combine(_configurationDirectoryPath, ConfigurationFileName);
|
|||
|
|
|
|||
|
|
EnsureConfigurationFile();
|
|||
|
|
LoadFromFile();
|
|||
|
|
|
|||
|
|
_fileWatcher = new FileSystemWatcher(_configurationDirectoryPath, ConfigurationFileName);
|
|||
|
|
_fileWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.CreationTime;
|
|||
|
|
_fileWatcher.Changed += OnConfigurationFileChanged;
|
|||
|
|
_fileWatcher.Created += OnConfigurationFileChanged;
|
|||
|
|
_fileWatcher.Renamed += OnConfigurationFileRenamed;
|
|||
|
|
_fileWatcher.EnableRaisingEvents = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Thickness Margin
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return new Thickness(_leftMargin, _topMargin, 0.0, 0.0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Thickness DebugPanelMargin
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return new Thickness(_leftMargin, _topMargin + DefaultDebugPanelTopOffset, 0.0, 0.0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool IsDebugPanelVisible
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return _isDebugPanelVisible;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public double Step
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return _step;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string LeftMarginText
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return _leftMarginText;
|
|||
|
|
}
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
SetAndNotify(ref _leftMarginText, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string TopMarginText
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return _topMarginText;
|
|||
|
|
}
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
SetAndNotify(ref _topMarginText, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string StepText
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return _stepText;
|
|||
|
|
}
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
SetAndNotify(ref _stepText, value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string PositionSummary
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return string.Format("Left={0:0.##}, Top={1:0.##}, Step={2:0.##}", _leftMargin, _topMargin, _step);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Dispose()
|
|||
|
|
{
|
|||
|
|
_fileWatcher.Changed -= OnConfigurationFileChanged;
|
|||
|
|
_fileWatcher.Created -= OnConfigurationFileChanged;
|
|||
|
|
_fileWatcher.Renamed -= OnConfigurationFileRenamed;
|
|||
|
|
_fileWatcher.Dispose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void AdjustMargin(double leftOffset, double topOffset)
|
|||
|
|
{
|
|||
|
|
double leftMargin = _leftMargin + leftOffset;
|
|||
|
|
double topMargin = _topMargin + topOffset;
|
|||
|
|
|
|||
|
|
UpdateSettings(leftMargin, topMargin, _isDebugPanelVisible, _step);
|
|||
|
|
PersistCurrentConfiguration();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void HideDebugPanel()
|
|||
|
|
{
|
|||
|
|
UpdateSettings(_leftMargin, _topMargin, false, _step);
|
|||
|
|
PersistCurrentConfiguration();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void ApplyTextValues()
|
|||
|
|
{
|
|||
|
|
double leftMargin;
|
|||
|
|
double topMargin;
|
|||
|
|
double step;
|
|||
|
|
|
|||
|
|
bool canParseLeft = TryParseInput(LeftMarginText, _leftMargin, out leftMargin);
|
|||
|
|
bool canParseTop = TryParseInput(TopMarginText, _topMargin, out topMargin);
|
|||
|
|
bool canParseStep = TryParseInput(StepText, _step, out step);
|
|||
|
|
|
|||
|
|
if (!canParseStep)
|
|||
|
|
{
|
|||
|
|
step = _step;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
step = NormalizeStep(step);
|
|||
|
|
|
|||
|
|
UpdateSettings(leftMargin, topMargin, _isDebugPanelVisible, step);
|
|||
|
|
PersistCurrentConfiguration();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnConfigurationFileChanged(object sender, FileSystemEventArgs e)
|
|||
|
|
{
|
|||
|
|
ReloadOnUiThread();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnConfigurationFileRenamed(object sender, RenamedEventArgs e)
|
|||
|
|
{
|
|||
|
|
ReloadOnUiThread();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ReloadOnUiThread()
|
|||
|
|
{
|
|||
|
|
Application currentApplication = Application.Current;
|
|||
|
|
if (currentApplication == null)
|
|||
|
|
{
|
|||
|
|
LoadFromFile();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
currentApplication.Dispatcher.BeginInvoke(
|
|||
|
|
DispatcherPriority.Background,
|
|||
|
|
new Action(LoadFromFile));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void EnsureConfigurationFile()
|
|||
|
|
{
|
|||
|
|
if (!Directory.Exists(_configurationDirectoryPath))
|
|||
|
|
{
|
|||
|
|
Directory.CreateDirectory(_configurationDirectoryPath);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (File.Exists(_configurationFilePath))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
HeaderDeviceStatusOverlayPositionConfiguration configuration = new HeaderDeviceStatusOverlayPositionConfiguration();
|
|||
|
|
configuration.TopMargin = DefaultTopMargin;
|
|||
|
|
configuration.LeftMargin = DefaultLeftMargin;
|
|||
|
|
configuration.ShowDebugPanel = false;
|
|||
|
|
configuration.AdjustStep = DefaultAdjustStep;
|
|||
|
|
SaveConfiguration(configuration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void LoadFromFile()
|
|||
|
|
{
|
|||
|
|
HeaderDeviceStatusOverlayPositionConfiguration configuration = TryReadConfiguration();
|
|||
|
|
if (configuration == null)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double topMargin = NormalizeMargin(configuration.TopMargin, DefaultTopMargin);
|
|||
|
|
double leftMargin = NormalizeMargin(configuration.LeftMargin, DefaultLeftMargin);
|
|||
|
|
double adjustStep = NormalizeStep(configuration.AdjustStep);
|
|||
|
|
UpdateSettings(leftMargin, topMargin, configuration.ShowDebugPanel, adjustStep);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private HeaderDeviceStatusOverlayPositionConfiguration TryReadConfiguration()
|
|||
|
|
{
|
|||
|
|
for (int attempt = 0; attempt < 3; attempt++)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
string content = File.ReadAllText(_configurationFilePath);
|
|||
|
|
if (string.IsNullOrWhiteSpace(content))
|
|||
|
|
{
|
|||
|
|
return new HeaderDeviceStatusOverlayPositionConfiguration();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
HeaderDeviceStatusOverlayPositionConfiguration configuration = JsonConvert.DeserializeObject<HeaderDeviceStatusOverlayPositionConfiguration>(content);
|
|||
|
|
return configuration ?? new HeaderDeviceStatusOverlayPositionConfiguration();
|
|||
|
|
}
|
|||
|
|
catch (IOException)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
catch (UnauthorizedAccessException)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
catch (JsonException)
|
|||
|
|
{
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void SaveConfiguration(HeaderDeviceStatusOverlayPositionConfiguration configuration)
|
|||
|
|
{
|
|||
|
|
string content = JsonConvert.SerializeObject(configuration, Formatting.Indented);
|
|||
|
|
File.WriteAllText(_configurationFilePath, content);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void PersistCurrentConfiguration()
|
|||
|
|
{
|
|||
|
|
HeaderDeviceStatusOverlayPositionConfiguration configuration = new HeaderDeviceStatusOverlayPositionConfiguration();
|
|||
|
|
configuration.TopMargin = _topMargin;
|
|||
|
|
configuration.LeftMargin = _leftMargin;
|
|||
|
|
configuration.ShowDebugPanel = _isDebugPanelVisible;
|
|||
|
|
configuration.AdjustStep = _step;
|
|||
|
|
SaveConfiguration(configuration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static double NormalizeMargin(double value, double fallbackValue)
|
|||
|
|
{
|
|||
|
|
if (double.IsNaN(value) || double.IsInfinity(value))
|
|||
|
|
{
|
|||
|
|
return fallbackValue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static double NormalizeStep(double value)
|
|||
|
|
{
|
|||
|
|
if (double.IsNaN(value) || double.IsInfinity(value) || value <= 0.0)
|
|||
|
|
{
|
|||
|
|
return DefaultAdjustStep;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void UpdateSettings(double leftMargin, double topMargin, bool isDebugPanelVisible, double step)
|
|||
|
|
{
|
|||
|
|
bool hasChanged = !leftMargin.Equals(_leftMargin)
|
|||
|
|
|| !topMargin.Equals(_topMargin)
|
|||
|
|
|| !isDebugPanelVisible.Equals(_isDebugPanelVisible)
|
|||
|
|
|| !step.Equals(_step);
|
|||
|
|
if (!hasChanged)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_leftMargin = leftMargin;
|
|||
|
|
_topMargin = topMargin;
|
|||
|
|
_isDebugPanelVisible = isDebugPanelVisible;
|
|||
|
|
_step = step;
|
|||
|
|
LeftMarginText = FormatValue(_leftMargin);
|
|||
|
|
TopMarginText = FormatValue(_topMargin);
|
|||
|
|
StepText = FormatValue(_step);
|
|||
|
|
NotifyOfPropertyChange(nameof(Margin));
|
|||
|
|
NotifyOfPropertyChange(nameof(DebugPanelMargin));
|
|||
|
|
NotifyOfPropertyChange(nameof(IsDebugPanelVisible));
|
|||
|
|
NotifyOfPropertyChange(nameof(Step));
|
|||
|
|
NotifyOfPropertyChange(nameof(PositionSummary));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool TryParseInput(string text, double fallbackValue, out double value)
|
|||
|
|
{
|
|||
|
|
string trimmedText = string.IsNullOrWhiteSpace(text) ? string.Empty : text.Trim();
|
|||
|
|
if (string.IsNullOrEmpty(trimmedText))
|
|||
|
|
{
|
|||
|
|
value = fallbackValue;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return double.TryParse(trimmedText, NumberStyles.Float, CultureInfo.InvariantCulture, out value)
|
|||
|
|
|| double.TryParse(trimmedText, NumberStyles.Float, CultureInfo.CurrentCulture, out value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static string FormatValue(double value)
|
|||
|
|
{
|
|||
|
|
return value.ToString("0.##", CultureInfo.InvariantCulture);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private sealed class HeaderDeviceStatusOverlayPositionConfiguration
|
|||
|
|
{
|
|||
|
|
public double TopMargin { get; set; }
|
|||
|
|
|
|||
|
|
public double LeftMargin { get; set; }
|
|||
|
|
|
|||
|
|
public bool ShowDebugPanel { get; set; }
|
|||
|
|
|
|||
|
|
public double AdjustStep { get; set; }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|