diff --git a/.gitignore b/.gitignore index 20af7cd..3920243 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ PCPalService/service_log.txt /PCPal/Configurator/obj /PCPal/Core/bin /PCPal/Core/obj +/PCPal/Configurator/bin diff --git a/PCPal/Configurator/AppShell.xaml b/PCPal/Configurator/AppShell.xaml index ebb1483..41101e0 100644 --- a/PCPal/Configurator/AppShell.xaml +++ b/PCPal/Configurator/AppShell.xaml @@ -1,72 +1,77 @@ - - - - - + xmlns:views="clr-namespace:PCPal.Configurator.Views" + x:Class="PCPal.Configurator.AppShell" + Shell.NavBarIsVisible="False" + Shell.FlyoutBehavior="Disabled"> - - - - + + + + + - - - - - 1602 LCD Display - 4.6" TFT Display - OLED Display - Settings - Help - - - - - - - - - - - - - - - - - - - - - - + + + + + 1602 LCD Display + 4.6" TFT Display + OLED Display + Settings + Help + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PCPal/Configurator/AppShell.xaml.cs b/PCPal/Configurator/AppShell.xaml.cs index a39d112..a2ddfbd 100644 --- a/PCPal/Configurator/AppShell.xaml.cs +++ b/PCPal/Configurator/AppShell.xaml.cs @@ -5,7 +5,6 @@ using PCPal.Configurator.Views.LCD; using PCPal.Configurator.Views.OLED; using PCPal.Configurator.Views.TFT; using System.ComponentModel; -//using UIKit; namespace PCPal.Configurator; @@ -14,6 +13,7 @@ public partial class AppShell : Shell, INotifyPropertyChanged private bool _isConnected; private string _connectionStatus; private DateTime _lastUpdateTime; + private CancellationTokenSource _monitoringCts; private readonly IServiceProvider _serviceProvider; @@ -60,7 +60,8 @@ public partial class AppShell : Shell, INotifyPropertyChanged { InitializeComponent(); - _serviceProvider = IPlatformApplication.Current.Services; + _serviceProvider = IPlatformApplication.Current?.Services; + _monitoringCts = new CancellationTokenSource(); // Set initial connection status IsConnected = false; @@ -74,17 +75,29 @@ public partial class AppShell : Shell, INotifyPropertyChanged StartConnectivityMonitoring(); } + protected override void OnDisappearing() + { + base.OnDisappearing(); + _monitoringCts?.Cancel(); + } + + ~AppShell() + { + _monitoringCts?.Cancel(); + _monitoringCts?.Dispose(); + } + private void OnNavMenuSelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection.FirstOrDefault() is string selection) { ContentView view = selection switch { - "1602 LCD Display" => _serviceProvider.GetService(), - "4.6 TFT Display" => _serviceProvider.GetService(), - "OLED Display" => _serviceProvider.GetService(), - "Settings" => _serviceProvider.GetService(), - "Help" => _serviceProvider.GetService(), + "1602 LCD Display" => _serviceProvider?.GetService(), + "4.6\" TFT Display" => _serviceProvider?.GetService(), + "OLED Display" => _serviceProvider?.GetService(), + "Settings" => _serviceProvider?.GetService(), + "Help" => _serviceProvider?.GetService(), _ => null }; @@ -97,7 +110,7 @@ public partial class AppShell : Shell, INotifyPropertyChanged private async void StartConnectivityMonitoring() { - var serialPortService = _serviceProvider.GetService(); + var serialPortService = _serviceProvider?.GetService(); if (serialPortService != null) { // Subscribe to connection status changes @@ -112,19 +125,31 @@ public partial class AppShell : Shell, INotifyPropertyChanged }; // Start periodic connection check - while (true) + try { - await Task.Delay(5000); - try + while (!_monitoringCts.Token.IsCancellationRequested) { - await serialPortService.CheckConnectionAsync(); - } - catch (Exception ex) - { - // Log error but don't crash the app - System.Diagnostics.Debug.WriteLine($"Connection check error: {ex.Message}"); + await Task.Delay(5000, _monitoringCts.Token); + + if (_monitoringCts.Token.IsCancellationRequested) + break; + + try + { + await serialPortService.CheckConnectionAsync(); + } + catch (Exception ex) + { + // Log error but don't crash the app + System.Diagnostics.Debug.WriteLine($"Connection check error: {ex.Message}"); + } } } + catch (OperationCanceledException) + { + // Expected when cancellation is requested + System.Diagnostics.Debug.WriteLine("Connectivity monitoring canceled"); + } } } } \ No newline at end of file diff --git a/PCPal/Configurator/Controls/OledPreviewCanvas.cs b/PCPal/Configurator/Controls/OledPreviewCanvas.cs index c86455e..3de217b 100644 --- a/PCPal/Configurator/Controls/OledPreviewCanvas.cs +++ b/PCPal/Configurator/Controls/OledPreviewCanvas.cs @@ -1,5 +1,4 @@ -//using Android.Sax; -using Microsoft.Maui.Controls.Shapes; +using Microsoft.Maui.Controls.Shapes; using PCPal.Configurator.ViewModels; using PCPal.Core.Models; using System.Collections.ObjectModel; @@ -162,138 +161,142 @@ public class OledPreviewCanvas : GraphicsView dragStartPoint = point; // Check if an element was clicked - if (Elements != null) + if (Elements == null || Elements.Count == 0) return; + + // Need to adjust for scale + float x = (float)point.X / Scale; + float y = (float)point.Y / Scale; + + // Check in reverse order (top elements first) + for (int i = Elements.Count - 1; i >= 0; i--) { - // Need to adjust for scale - float x = (float)point.X / Scale; - float y = (float)point.Y / Scale; + var element = Elements[i]; - foreach (var element in Elements) + if (element is TextElement textElement) { - if (element is TextElement textElement) + // Simple bounding box check + if (x >= textElement.X && x <= textElement.X + 100 && + y >= textElement.Y - 20 && y <= textElement.Y) { - // Simple bounding box check - if (x >= textElement.X && x <= textElement.X + 100 && - y >= textElement.Y - 20 && y <= textElement.Y) + // Find the OledElement that corresponds to this PreviewElement + var oledElement = FindOledElementForPreviewElement(textElement); + if (oledElement != null) { - // Find the OledElement that corresponds to this PreviewElement - var oledElement = FindOledElementForPreviewElement(textElement); - if (oledElement != null) - { - draggedElement = oledElement; - SelectedElement = oledElement; - return; - } - } - } - else if (element is BarElement barElement) - { - if (x >= barElement.X && x <= barElement.X + barElement.Width && - y >= barElement.Y && y <= barElement.Y + barElement.Height) - { - var oledElement = FindOledElementForPreviewElement(barElement); - if (oledElement != null) - { - draggedElement = oledElement; - SelectedElement = oledElement; - return; - } - } - } - else if (element is RectElement rectElement) - { - if (x >= rectElement.X && x <= rectElement.X + rectElement.Width && - y >= rectElement.Y && y <= rectElement.Y + rectElement.Height) - { - var oledElement = FindOledElementForPreviewElement(rectElement); - if (oledElement != null) - { - draggedElement = oledElement; - SelectedElement = oledElement; - return; - } - } - } - else if (element is LineElement lineElement) - { - // Simplified line hit detection - float lineLength = (float)Math.Sqrt( - Math.Pow(lineElement.X2 - lineElement.X1, 2) + - Math.Pow(lineElement.Y2 - lineElement.Y1, 2)); - - // Check if point is close to the line - float distance = DistancePointToLine( - x, y, - lineElement.X1, lineElement.Y1, - lineElement.X2, lineElement.Y2); - - if (distance < 10) // 10 pixel tolerance - { - var oledElement = FindOledElementForPreviewElement(lineElement); - if (oledElement != null) - { - draggedElement = oledElement; - SelectedElement = oledElement; - return; - } - } - } - else if (element is IconElement iconElement) - { - if (x >= iconElement.X && x <= iconElement.X + 24 && - y >= iconElement.Y && y <= iconElement.Y + 24) - { - var oledElement = FindOledElementForPreviewElement(iconElement); - if (oledElement != null) - { - draggedElement = oledElement; - SelectedElement = oledElement; - return; - } + draggedElement = oledElement; + SelectedElement = oledElement; + return; } } } + else if (element is BarElement barElement) + { + if (x >= barElement.X && x <= barElement.X + barElement.Width && + y >= barElement.Y && y <= barElement.Y + barElement.Height) + { + var oledElement = FindOledElementForPreviewElement(barElement); + if (oledElement != null) + { + draggedElement = oledElement; + SelectedElement = oledElement; + return; + } + } + } + else if (element is RectElement rectElement) + { + if (x >= rectElement.X && x <= rectElement.X + rectElement.Width && + y >= rectElement.Y && y <= rectElement.Y + rectElement.Height) + { + var oledElement = FindOledElementForPreviewElement(rectElement); + if (oledElement != null) + { + draggedElement = oledElement; + SelectedElement = oledElement; + return; + } + } + } + else if (element is LineElement lineElement) + { + // Simplified line hit detection + float distance = DistancePointToLine( + x, y, + lineElement.X1, lineElement.Y1, + lineElement.X2, lineElement.Y2); - // No element was clicked, deselect - SelectedElement = null; + if (distance < 10) // 10 pixel tolerance + { + var oledElement = FindOledElementForPreviewElement(lineElement); + if (oledElement != null) + { + draggedElement = oledElement; + SelectedElement = oledElement; + return; + } + } + } + else if (element is IconElement iconElement) + { + if (x >= iconElement.X && x <= iconElement.X + 24 && + y >= iconElement.Y && y <= iconElement.Y + 24) + { + var oledElement = FindOledElementForPreviewElement(iconElement); + if (oledElement != null) + { + draggedElement = oledElement; + SelectedElement = oledElement; + return; + } + } + } } + + // No element was clicked, deselect + SelectedElement = null; } private void OnDragInteraction(object sender, TouchEventArgs e) { if (!IsEditable || draggedElement == null) return; - var point = e.Touches[0]; - - // Calculate the delta from the start point - float deltaX = (float)(point.X - dragStartPoint.X) / Scale; - float deltaY = (float)(point.Y - dragStartPoint.Y) / Scale; - - // Update the position of the dragged element - draggedElement.X += (int)deltaX; - draggedElement.Y += (int)deltaY; - - // Keep element within bounds - draggedElement.X = Math.Max(0, Math.Min(Width - 10, draggedElement.X)); - draggedElement.Y = Math.Max(0, Math.Min(Height - 10, draggedElement.Y)); - - // Update the start point for the next move - dragStartPoint = point; - - // Notify property changes - var viewModel = BindingContext as OledConfigViewModel; - if (viewModel != null) + try { - // Update the view model properties to reflect the new position - viewModel.OnPropertyChanged(nameof(viewModel.SelectedElementX)); - viewModel.OnPropertyChanged(nameof(viewModel.SelectedElementY)); + var point = e.Touches[0]; - // Update the markup - viewModel.UpdateMarkupFromElements(); + // Calculate the delta from the start point + float deltaX = (float)(point.X - dragStartPoint.X) / Scale; + float deltaY = (float)(point.Y - dragStartPoint.Y) / Scale; + + // Update the position of the dragged element + draggedElement.X += (int)deltaX; + draggedElement.Y += (int)deltaY; + + // Keep element within bounds + draggedElement.X = Math.Max(0, Math.Min(Width - 10, draggedElement.X)); + draggedElement.Y = Math.Max(0, Math.Min(Height - 10, draggedElement.Y)); + + // Update the start point for the next move + dragStartPoint = point; + + // Notify property changes + var viewModel = BindingContext as PCPal.Configurator.ViewModels.OledConfigViewModel; + if (viewModel != null) + { + // Update the view model properties to reflect the new position + viewModel.OnPropertyChanged(nameof(viewModel.SelectedElementX)); + viewModel.OnPropertyChanged(nameof(viewModel.SelectedElementY)); + + // Update the markup + viewModel.UpdateMarkupFromElements(); + } + + // Invalidate the canvas to redraw + Invalidate(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Error in drag interaction: {ex.Message}"); } - - // Invalidate the canvas to redraw - Invalidate(); } private void OnEndInteraction(object sender, TouchEventArgs e) @@ -378,66 +381,73 @@ public class OledCanvasDrawable : IDrawable public void Draw(ICanvas canvas, RectF dirtyRect) { - float scale = _canvas.Scale; - - // Clear background - canvas.FillColor = Colors.Black; - canvas.FillRectangle(0, 0, dirtyRect.Width, dirtyRect.Height); - - // Draw grid if requested - if (_canvas.IsEditable && _canvas.Parent?.BindingContext is OledConfigViewModel viewModel && viewModel.ShowGridLines) + try { - canvas.StrokeColor = new Color(64, 64, 64, 64); // Semi-transparent gray - canvas.StrokeSize = 1; + float scale = _canvas.Scale; - // Draw vertical grid lines - for (int x = 0; x <= _canvas.Width; x += 10) - { - canvas.DrawLine(x * scale, 0, x * scale, _canvas.Height * scale); - } + // Clear background + canvas.FillColor = Colors.Black; + canvas.FillRectangle(0, 0, dirtyRect.Width, dirtyRect.Height); - // Draw horizontal grid lines - for (int y = 0; y <= _canvas.Height; y += 10) + // Draw grid if requested + if (_canvas.IsEditable && _canvas.Parent?.BindingContext is OledConfigViewModel viewModel && viewModel.ShowGridLines) { - canvas.DrawLine(0, y * scale, _canvas.Width * scale, y * scale); - } - } + canvas.StrokeColor = new Color(64, 64, 64, 64); // Semi-transparent gray + canvas.StrokeSize = 1; - // Draw elements - if (_canvas.Elements != null) - { - foreach (var element in _canvas.Elements) - { - // Check if this element is selected - bool isSelected = false; - if (_canvas.SelectedElement != null && _canvas.IsEditable) + // Draw vertical grid lines + for (int x = 0; x <= _canvas.Width; x += 10) { - if (element is TextElement textElement && _canvas.SelectedElement.Type == "text") - { - isSelected = _canvas.SelectedElement.X == textElement.X && _canvas.SelectedElement.Y == textElement.Y; - } - else if (element is BarElement barElement && _canvas.SelectedElement.Type == "bar") - { - isSelected = _canvas.SelectedElement.X == barElement.X && _canvas.SelectedElement.Y == barElement.Y; - } - else if (element is RectElement rectElement && - (_canvas.SelectedElement.Type == "rect" || _canvas.SelectedElement.Type == "box")) - { - isSelected = _canvas.SelectedElement.X == rectElement.X && _canvas.SelectedElement.Y == rectElement.Y; - } - else if (element is LineElement lineElement && _canvas.SelectedElement.Type == "line") - { - isSelected = _canvas.SelectedElement.X == lineElement.X1 && _canvas.SelectedElement.Y == lineElement.Y1; - } - else if (element is IconElement iconElement && _canvas.SelectedElement.Type == "icon") - { - isSelected = _canvas.SelectedElement.X == iconElement.X && _canvas.SelectedElement.Y == iconElement.Y; - } + canvas.DrawLine(x * scale, 0, x * scale, _canvas.Height * scale); } - // Draw the element with appropriate styling - DrawElement(canvas, element, scale, isSelected); + // Draw horizontal grid lines + for (int y = 0; y <= _canvas.Height; y += 10) + { + canvas.DrawLine(0, y * scale, _canvas.Width * scale, y * scale); + } } + + // Draw elements + if (_canvas.Elements != null) + { + foreach (var element in _canvas.Elements) + { + // Check if this element is selected + bool isSelected = false; + if (_canvas.SelectedElement != null && _canvas.IsEditable) + { + if (element is TextElement textElement && _canvas.SelectedElement.Type == "text") + { + isSelected = _canvas.SelectedElement.X == textElement.X && _canvas.SelectedElement.Y == textElement.Y; + } + else if (element is BarElement barElement && _canvas.SelectedElement.Type == "bar") + { + isSelected = _canvas.SelectedElement.X == barElement.X && _canvas.SelectedElement.Y == barElement.Y; + } + else if (element is RectElement rectElement && + (_canvas.SelectedElement.Type == "rect" || _canvas.SelectedElement.Type == "box")) + { + isSelected = _canvas.SelectedElement.X == rectElement.X && _canvas.SelectedElement.Y == rectElement.Y; + } + else if (element is LineElement lineElement && _canvas.SelectedElement.Type == "line") + { + isSelected = _canvas.SelectedElement.X == lineElement.X1 && _canvas.SelectedElement.Y == lineElement.Y1; + } + else if (element is IconElement iconElement && _canvas.SelectedElement.Type == "icon") + { + isSelected = _canvas.SelectedElement.X == iconElement.X && _canvas.SelectedElement.Y == iconElement.Y; + } + } + + // Draw the element with appropriate styling + DrawElement(canvas, element, scale, isSelected); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Error drawing canvas: {ex.Message}"); } } diff --git a/PCPal/Configurator/Resources/Styles/Colors.xaml b/PCPal/Configurator/Resources/Styles/Colors.xaml index 30307a5..2850f03 100644 --- a/PCPal/Configurator/Resources/Styles/Colors.xaml +++ b/PCPal/Configurator/Resources/Styles/Colors.xaml @@ -6,12 +6,20 @@ - #512BD4 - #ac99ea - #242424 - #DFD8F7 - #9880e5 + #1E88E5 + #1565C0 + #E3F2FD + #CFD8DC #2B0B98 + #03A9F4 + #F4F6F8 + #FFFFFF + #333333 + #555555 + #E1E4E8 + #4CAF50 + #F44336 + #FF9800 White Black diff --git a/PCPal/Configurator/Resources/Styles/Converters.xaml.cs b/PCPal/Configurator/Resources/Styles/Converters.xaml.cs index f39faef..f2d0f53 100644 --- a/PCPal/Configurator/Resources/Styles/Converters.xaml.cs +++ b/PCPal/Configurator/Resources/Styles/Converters.xaml.cs @@ -83,15 +83,21 @@ public class StringMatchConverter : IValueConverter { if (currentValue == targetValue) { - // Handle different return types based on an additional parameter - if (parameter is string param2 && param2 == "TextColor") + // Default to background coloring + Color returnColor = Application.Current.Resources["PrimaryLight"] as Color ?? Colors.LightBlue; + + // Check for additional parameter context + if (targetValue.EndsWith(":TextColor")) { - // Return text color for selected item - return Application.Current.Resources["Primary"] as Color ?? Colors.Black; + // Strip the ":TextColor" suffix before comparison + string strippedTarget = targetValue.Substring(0, targetValue.Length - 10); + if (currentValue == strippedTarget) + { + return Application.Current.Resources["Primary"] as Color ?? Colors.Black; + } } - // Return background color for selected item - return Application.Current.Resources["PrimaryLight"] as Color ?? Colors.LightBlue; + return returnColor; } } diff --git a/PCPal/Configurator/Resources/Styles/Styles.xaml b/PCPal/Configurator/Resources/Styles/Styles.xaml index 4e3b560..6982355 100644 --- a/PCPal/Configurator/Resources/Styles/Styles.xaml +++ b/PCPal/Configurator/Resources/Styles/Styles.xaml @@ -86,12 +86,6 @@ - -