Files
test_demo/MX-PD-盘古 - new/PanGu.DieBonderApp/MainShell/Common/HeaderDeviceStatusOverlayHost.cs
Shi.Ji e31d3560bb 添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
2026-05-18 11:43:09 +08:00

607 lines
24 KiB
C#
Raw Blame History

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; }
}
}
}