defect updates, bug still in code
This commit is contained in:
@@ -5,7 +5,6 @@ using PCPal.Configurator.Views.LCD;
|
||||
using PCPal.Configurator.Views.OLED;
|
||||
using PCPal.Configurator.Views.TFT;
|
||||
using System.ComponentModel;
|
||||
using PCPal.Configurator.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace PCPal.Configurator;
|
||||
|
||||
242
PCPal/Configurator/Controls/OledDisplayCanvas.cs
Normal file
242
PCPal/Configurator/Controls/OledDisplayCanvas.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using Microsoft.Maui.Controls.Shapes;
|
||||
using PCPal.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace PCPal.Configurator.Controls;
|
||||
|
||||
// This is a new control that only handles display functionality - no editing
|
||||
public class OledDisplayCanvas : GraphicsView
|
||||
{
|
||||
// Bindable properties for the control
|
||||
public static readonly BindableProperty ElementsProperty = BindableProperty.Create(
|
||||
nameof(Elements),
|
||||
typeof(IList<PreviewElement>),
|
||||
typeof(OledDisplayCanvas),
|
||||
null,
|
||||
propertyChanged: OnElementsChanged);
|
||||
|
||||
public static readonly BindableProperty ScaleProperty = BindableProperty.Create(
|
||||
nameof(Scale),
|
||||
typeof(float),
|
||||
typeof(OledDisplayCanvas),
|
||||
1.0f,
|
||||
propertyChanged: OnScaleChanged);
|
||||
|
||||
public static readonly BindableProperty CanvasWidthProperty = BindableProperty.Create(
|
||||
nameof(CanvasWidth),
|
||||
typeof(int),
|
||||
typeof(OledDisplayCanvas),
|
||||
256);
|
||||
|
||||
public static readonly BindableProperty CanvasHeightProperty = BindableProperty.Create(
|
||||
nameof(CanvasHeight),
|
||||
typeof(int),
|
||||
typeof(OledDisplayCanvas),
|
||||
64);
|
||||
|
||||
// Property accessors
|
||||
public IList<PreviewElement> Elements
|
||||
{
|
||||
get => (IList<PreviewElement>)GetValue(ElementsProperty);
|
||||
set => SetValue(ElementsProperty, value);
|
||||
}
|
||||
|
||||
public float Scale
|
||||
{
|
||||
get => (float)GetValue(ScaleProperty);
|
||||
set => SetValue(ScaleProperty, value);
|
||||
}
|
||||
|
||||
public int CanvasWidth
|
||||
{
|
||||
get => (int)GetValue(CanvasWidthProperty);
|
||||
set => SetValue(CanvasWidthProperty, value);
|
||||
}
|
||||
|
||||
public int CanvasHeight
|
||||
{
|
||||
get => (int)GetValue(CanvasHeightProperty);
|
||||
set => SetValue(CanvasHeightProperty, value);
|
||||
}
|
||||
|
||||
// Constructor
|
||||
public OledDisplayCanvas()
|
||||
{
|
||||
// Set default drawing
|
||||
Drawable = new OledDisplayDrawable(this);
|
||||
|
||||
// Set up initial size
|
||||
WidthRequest = 256 * Scale;
|
||||
HeightRequest = 64 * Scale;
|
||||
}
|
||||
|
||||
// Element collection change handler
|
||||
private static void OnElementsChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var canvas = (OledDisplayCanvas)bindable;
|
||||
|
||||
// If old value is INotifyCollectionChanged, unsubscribe
|
||||
if (oldValue is INotifyCollectionChanged oldCollection)
|
||||
{
|
||||
oldCollection.CollectionChanged -= canvas.OnCollectionChanged;
|
||||
}
|
||||
|
||||
// If new value is INotifyCollectionChanged, subscribe
|
||||
if (newValue is INotifyCollectionChanged newCollection)
|
||||
{
|
||||
newCollection.CollectionChanged += canvas.OnCollectionChanged;
|
||||
}
|
||||
|
||||
// Invalidate the canvas to redraw
|
||||
canvas.Invalidate();
|
||||
}
|
||||
|
||||
// Scale change handler
|
||||
private static void OnScaleChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var canvas = (OledDisplayCanvas)bindable;
|
||||
float scale = (float)newValue;
|
||||
|
||||
// Update the size of the canvas based on the scale
|
||||
canvas.WidthRequest = canvas.CanvasWidth * scale;
|
||||
canvas.HeightRequest = canvas.CanvasHeight * scale;
|
||||
|
||||
canvas.Invalidate();
|
||||
}
|
||||
|
||||
// Collection changed event handler
|
||||
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// The drawable that renders the OLED canvas
|
||||
public class OledDisplayDrawable : IDrawable
|
||||
{
|
||||
private readonly OledDisplayCanvas _canvas;
|
||||
|
||||
public OledDisplayDrawable(OledDisplayCanvas canvas)
|
||||
{
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
public void Draw(ICanvas canvas, RectF dirtyRect)
|
||||
{
|
||||
try
|
||||
{
|
||||
float scale = _canvas.Scale;
|
||||
|
||||
// Clear background
|
||||
canvas.FillColor = Colors.Black;
|
||||
canvas.FillRectangle(0, 0, dirtyRect.Width, dirtyRect.Height);
|
||||
|
||||
// Draw elements
|
||||
if (_canvas.Elements != null)
|
||||
{
|
||||
foreach (var element in _canvas.Elements)
|
||||
{
|
||||
DrawElement(canvas, element, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Error drawing canvas: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawElement(ICanvas canvas, PreviewElement element, float scale)
|
||||
{
|
||||
canvas.StrokeColor = Colors.White;
|
||||
canvas.StrokeSize = 1;
|
||||
canvas.FillColor = Colors.White;
|
||||
|
||||
if (element is TextElement textElement)
|
||||
{
|
||||
float fontSize;
|
||||
switch (textElement.Size)
|
||||
{
|
||||
case 1: fontSize = 8 * scale; break;
|
||||
case 2: fontSize = 12 * scale; break;
|
||||
case 3: fontSize = 16 * scale; break;
|
||||
default: fontSize = 8 * scale; break;
|
||||
}
|
||||
|
||||
canvas.FontSize = fontSize;
|
||||
canvas.FontColor = Colors.White;
|
||||
canvas.DrawString(
|
||||
textElement.Text,
|
||||
textElement.X * scale,
|
||||
textElement.Y * scale,
|
||||
HorizontalAlignment.Left);
|
||||
}
|
||||
else if (element is BarElement barElement)
|
||||
{
|
||||
// Draw outline
|
||||
canvas.DrawRectangle(
|
||||
barElement.X * scale,
|
||||
barElement.Y * scale,
|
||||
barElement.Width * scale,
|
||||
barElement.Height * scale);
|
||||
|
||||
// Draw fill based on value
|
||||
int fillWidth = (int)(barElement.Width * (barElement.Value / 100.0));
|
||||
if (fillWidth > 0)
|
||||
{
|
||||
canvas.FillRectangle(
|
||||
(barElement.X + 1) * scale,
|
||||
(barElement.Y + 1) * scale,
|
||||
(fillWidth - 1) * scale,
|
||||
(barElement.Height - 2) * scale);
|
||||
}
|
||||
}
|
||||
else if (element is RectElement rectElement)
|
||||
{
|
||||
if (rectElement.Filled)
|
||||
{
|
||||
// Filled box
|
||||
canvas.FillRectangle(
|
||||
rectElement.X * scale,
|
||||
rectElement.Y * scale,
|
||||
rectElement.Width * scale,
|
||||
rectElement.Height * scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outline rectangle
|
||||
canvas.DrawRectangle(
|
||||
rectElement.X * scale,
|
||||
rectElement.Y * scale,
|
||||
rectElement.Width * scale,
|
||||
rectElement.Height * scale);
|
||||
}
|
||||
}
|
||||
else if (element is LineElement lineElement)
|
||||
{
|
||||
canvas.DrawLine(
|
||||
lineElement.X1 * scale,
|
||||
lineElement.Y1 * scale,
|
||||
lineElement.X2 * scale,
|
||||
lineElement.Y2 * scale);
|
||||
}
|
||||
else if (element is IconElement iconElement)
|
||||
{
|
||||
// Draw a placeholder for the icon
|
||||
canvas.DrawRectangle(
|
||||
iconElement.X * scale,
|
||||
iconElement.Y * scale,
|
||||
24 * scale,
|
||||
24 * scale);
|
||||
|
||||
// Draw icon name as text
|
||||
canvas.FontSize = 8 * scale;
|
||||
canvas.DrawString(
|
||||
iconElement.Name,
|
||||
(iconElement.X + 2) * scale,
|
||||
(iconElement.Y + 12) * scale,
|
||||
HorizontalAlignment.Left);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Maui.Controls.Shapes;
|
||||
using PCPal.Configurator.ViewModels;
|
||||
using PCPal.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
@@ -16,20 +15,6 @@ public class OledPreviewCanvas : GraphicsView
|
||||
null,
|
||||
propertyChanged: OnElementsChanged);
|
||||
|
||||
public static readonly BindableProperty SelectedElementProperty = BindableProperty.Create(
|
||||
nameof(SelectedElement),
|
||||
typeof(OledElement),
|
||||
typeof(OledPreviewCanvas),
|
||||
null,
|
||||
BindingMode.TwoWay,
|
||||
propertyChanged: OnSelectedElementChanged);
|
||||
|
||||
public static readonly BindableProperty IsEditableProperty = BindableProperty.Create(
|
||||
nameof(IsEditable),
|
||||
typeof(bool),
|
||||
typeof(OledPreviewCanvas),
|
||||
false);
|
||||
|
||||
public static readonly BindableProperty ScaleProperty = BindableProperty.Create(
|
||||
nameof(Scale),
|
||||
typeof(float),
|
||||
@@ -56,18 +41,6 @@ public class OledPreviewCanvas : GraphicsView
|
||||
set => SetValue(ElementsProperty, value);
|
||||
}
|
||||
|
||||
public OledElement SelectedElement
|
||||
{
|
||||
get => (OledElement)GetValue(SelectedElementProperty);
|
||||
set => SetValue(SelectedElementProperty, value);
|
||||
}
|
||||
|
||||
public bool IsEditable
|
||||
{
|
||||
get => (bool)GetValue(IsEditableProperty);
|
||||
set => SetValue(IsEditableProperty, value);
|
||||
}
|
||||
|
||||
public float Scale
|
||||
{
|
||||
get => (float)GetValue(ScaleProperty);
|
||||
@@ -92,11 +65,6 @@ public class OledPreviewCanvas : GraphicsView
|
||||
// Set default drawing
|
||||
Drawable = new OledCanvasDrawable(this);
|
||||
|
||||
// Set up interaction handlers if editable
|
||||
StartInteraction += OnStartInteraction;
|
||||
DragInteraction += OnDragInteraction;
|
||||
EndInteraction += OnEndInteraction;
|
||||
|
||||
// Set up initial size
|
||||
WidthRequest = 256 * Scale;
|
||||
HeightRequest = 64 * Scale;
|
||||
@@ -123,13 +91,6 @@ public class OledPreviewCanvas : GraphicsView
|
||||
canvas.Invalidate();
|
||||
}
|
||||
|
||||
// Selected element change handler
|
||||
private static void OnSelectedElementChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var canvas = (OledPreviewCanvas)bindable;
|
||||
canvas.Invalidate();
|
||||
}
|
||||
|
||||
// Scale change handler
|
||||
private static void OnScaleChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
@@ -148,225 +109,6 @@ public class OledPreviewCanvas : GraphicsView
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
// Interaction handlers for element selection and manipulation
|
||||
private OledElement draggedElement;
|
||||
private Point dragStartPoint;
|
||||
|
||||
private void OnStartInteraction(object sender, TouchEventArgs e)
|
||||
{
|
||||
if (!IsEditable) return;
|
||||
|
||||
var point = e.Touches[0];
|
||||
dragStartPoint = point;
|
||||
|
||||
// Check if an element was clicked
|
||||
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--)
|
||||
{
|
||||
var element = Elements[i];
|
||||
|
||||
if (element is TextElement textElement)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
try
|
||||
{
|
||||
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 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}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEndInteraction(object sender, TouchEventArgs e)
|
||||
{
|
||||
draggedElement = null;
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
private OledElement FindOledElementForPreviewElement(PreviewElement previewElement)
|
||||
{
|
||||
var viewModel = BindingContext as OledConfigViewModel;
|
||||
if (viewModel == null || viewModel.OledElements == null) return null;
|
||||
|
||||
foreach (var oledElement in viewModel.OledElements)
|
||||
{
|
||||
// Match based on position and type
|
||||
if (previewElement is TextElement textElement && oledElement.Type == "text")
|
||||
{
|
||||
if (oledElement.X == textElement.X && oledElement.Y == textElement.Y)
|
||||
{
|
||||
return oledElement;
|
||||
}
|
||||
}
|
||||
else if (previewElement is BarElement barElement && oledElement.Type == "bar")
|
||||
{
|
||||
if (oledElement.X == barElement.X && oledElement.Y == barElement.Y)
|
||||
{
|
||||
return oledElement;
|
||||
}
|
||||
}
|
||||
else if (previewElement is RectElement rectElement)
|
||||
{
|
||||
if ((oledElement.Type == "rect" || oledElement.Type == "box") &&
|
||||
oledElement.X == rectElement.X && oledElement.Y == rectElement.Y)
|
||||
{
|
||||
return oledElement;
|
||||
}
|
||||
}
|
||||
else if (previewElement is LineElement lineElement && oledElement.Type == "line")
|
||||
{
|
||||
if (oledElement.X == lineElement.X1 && oledElement.Y == lineElement.Y1)
|
||||
{
|
||||
return oledElement;
|
||||
}
|
||||
}
|
||||
else if (previewElement is IconElement iconElement && oledElement.Type == "icon")
|
||||
{
|
||||
if (oledElement.X == iconElement.X && oledElement.Y == iconElement.Y)
|
||||
{
|
||||
return oledElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private float DistancePointToLine(float px, float py, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
float lineLength = (float)Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
|
||||
if (lineLength == 0) return (float)Math.Sqrt(Math.Pow(px - x1, 2) + Math.Pow(py - y1, 2));
|
||||
|
||||
float t = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / (lineLength * lineLength);
|
||||
t = Math.Max(0, Math.Min(1, t));
|
||||
|
||||
float projX = x1 + t * (x2 - x1);
|
||||
float projY = y1 + t * (y2 - y1);
|
||||
|
||||
return (float)Math.Sqrt(Math.Pow(px - projX, 2) + Math.Pow(py - projY, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// The drawable that renders the OLED canvas
|
||||
@@ -389,59 +131,12 @@ public class OledCanvasDrawable : IDrawable
|
||||
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)
|
||||
{
|
||||
canvas.StrokeColor = new Color(64, 64, 64, 64); // Semi-transparent gray
|
||||
canvas.StrokeSize = 1;
|
||||
|
||||
// Draw vertical grid lines
|
||||
for (int x = 0; x <= _canvas.Width; x += 10)
|
||||
{
|
||||
canvas.DrawLine(x * scale, 0, x * scale, _canvas.Height * scale);
|
||||
}
|
||||
|
||||
// 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);
|
||||
DrawElement(canvas, element, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,20 +146,10 @@ public class OledCanvasDrawable : IDrawable
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawElement(ICanvas canvas, PreviewElement element, float scale, bool isSelected)
|
||||
private void DrawElement(ICanvas canvas, PreviewElement element, float scale)
|
||||
{
|
||||
// Set selection highlighting if needed
|
||||
if (isSelected)
|
||||
{
|
||||
canvas.StrokeColor = Colors.Cyan;
|
||||
canvas.StrokeSize = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas.StrokeColor = Colors.White;
|
||||
canvas.StrokeSize = 1;
|
||||
}
|
||||
|
||||
canvas.StrokeColor = Colors.White;
|
||||
canvas.StrokeSize = 1;
|
||||
canvas.FillColor = Colors.White;
|
||||
|
||||
if (element is TextElement textElement)
|
||||
@@ -485,17 +170,6 @@ public class OledCanvasDrawable : IDrawable
|
||||
textElement.X * scale,
|
||||
textElement.Y * scale,
|
||||
HorizontalAlignment.Left);
|
||||
|
||||
// Draw selection indicator for text elements
|
||||
if (isSelected)
|
||||
{
|
||||
var metrics = canvas.GetStringSize(textElement.Text, Microsoft.Maui.Graphics.Font.Default, fontSize);
|
||||
canvas.DrawRectangle(
|
||||
textElement.X * scale - 2,
|
||||
textElement.Y * scale - metrics.Height - 2,
|
||||
metrics.Width + 4,
|
||||
metrics.Height + 4);
|
||||
}
|
||||
}
|
||||
else if (element is BarElement barElement)
|
||||
{
|
||||
@@ -516,17 +190,6 @@ public class OledCanvasDrawable : IDrawable
|
||||
(fillWidth - 1) * scale,
|
||||
(barElement.Height - 2) * scale);
|
||||
}
|
||||
|
||||
// Draw selection indicator
|
||||
if (isSelected)
|
||||
{
|
||||
canvas.StrokeColor = Colors.Cyan;
|
||||
canvas.DrawRectangle(
|
||||
(barElement.X - 2) * scale,
|
||||
(barElement.Y - 2) * scale,
|
||||
(barElement.Width + 4) * scale,
|
||||
(barElement.Height + 4) * scale);
|
||||
}
|
||||
}
|
||||
else if (element is RectElement rectElement)
|
||||
{
|
||||
@@ -548,17 +211,6 @@ public class OledCanvasDrawable : IDrawable
|
||||
rectElement.Width * scale,
|
||||
rectElement.Height * scale);
|
||||
}
|
||||
|
||||
// Draw selection indicator
|
||||
if (isSelected)
|
||||
{
|
||||
canvas.StrokeColor = Colors.Cyan;
|
||||
canvas.DrawRectangle(
|
||||
(rectElement.X - 2) * scale,
|
||||
(rectElement.Y - 2) * scale,
|
||||
(rectElement.Width + 4) * scale,
|
||||
(rectElement.Height + 4) * scale);
|
||||
}
|
||||
}
|
||||
else if (element is LineElement lineElement)
|
||||
{
|
||||
@@ -567,22 +219,6 @@ public class OledCanvasDrawable : IDrawable
|
||||
lineElement.Y1 * scale,
|
||||
lineElement.X2 * scale,
|
||||
lineElement.Y2 * scale);
|
||||
|
||||
// Draw selection indicator
|
||||
if (isSelected)
|
||||
{
|
||||
canvas.StrokeColor = Colors.Cyan;
|
||||
canvas.StrokeSize = 3;
|
||||
canvas.DrawLine(
|
||||
lineElement.X1 * scale,
|
||||
lineElement.Y1 * scale,
|
||||
lineElement.X2 * scale,
|
||||
lineElement.Y2 * scale);
|
||||
|
||||
// Draw endpoints
|
||||
canvas.FillCircle(lineElement.X1 * scale, lineElement.Y1 * scale, 4);
|
||||
canvas.FillCircle(lineElement.X2 * scale, lineElement.Y2 * scale, 4);
|
||||
}
|
||||
}
|
||||
else if (element is IconElement iconElement)
|
||||
{
|
||||
@@ -600,17 +236,6 @@ public class OledCanvasDrawable : IDrawable
|
||||
(iconElement.X + 2) * scale,
|
||||
(iconElement.Y + 12) * scale,
|
||||
HorizontalAlignment.Left);
|
||||
|
||||
// Draw selection indicator
|
||||
if (isSelected)
|
||||
{
|
||||
canvas.StrokeColor = Colors.Cyan;
|
||||
canvas.DrawRectangle(
|
||||
(iconElement.X - 2) * scale,
|
||||
(iconElement.Y - 2) * scale,
|
||||
(24 + 4) * scale,
|
||||
(24 + 4) * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ public static class MauiProgram
|
||||
// OLED
|
||||
builder.Services.AddTransient<OledConfigView>();
|
||||
builder.Services.AddTransient<OledConfigViewModel>();
|
||||
builder.Services.AddTransient<OledVisualEditorView>();
|
||||
builder.Services.AddTransient<OledMarkupEditorView>();
|
||||
builder.Services.AddTransient<OledTemplatesView>();
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics;
|
||||
//using Javax.Xml.Transform;
|
||||
|
||||
namespace PCPal.Configurator.ViewModels;
|
||||
|
||||
@@ -16,7 +15,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
private readonly ISerialPortService _serialPortService;
|
||||
|
||||
// Tab selection
|
||||
private bool _isVisualEditorSelected;
|
||||
private bool _isMarkupEditorSelected;
|
||||
private bool _isTemplatesSelected;
|
||||
private ContentView _currentView;
|
||||
@@ -25,14 +23,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
private string _oledMarkup;
|
||||
private List<PreviewElement> _previewElements;
|
||||
|
||||
// Visual editor data
|
||||
private ObservableCollection<OledElement> _oledElements;
|
||||
private OledElement _selectedElement;
|
||||
private bool _showGridLines;
|
||||
private float _zoomLevel;
|
||||
private string _currentSensorFilter;
|
||||
private ObservableCollection<SensorItem> _filteredSensors;
|
||||
|
||||
// Common properties
|
||||
private ObservableCollection<SensorItem> _availableSensors;
|
||||
|
||||
@@ -43,7 +33,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
private string _newTemplateName;
|
||||
|
||||
// Views
|
||||
private readonly OledVisualEditorView _visualEditorView;
|
||||
private readonly OledMarkupEditorView _markupEditorView;
|
||||
private readonly OledTemplatesView _templatesView;
|
||||
|
||||
@@ -54,12 +43,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
#region Properties
|
||||
|
||||
// Tab selection properties
|
||||
public bool IsVisualEditorSelected
|
||||
{
|
||||
get => _isVisualEditorSelected;
|
||||
set => SetProperty(ref _isVisualEditorSelected, value);
|
||||
}
|
||||
|
||||
public bool IsMarkupEditorSelected
|
||||
{
|
||||
get => _isMarkupEditorSelected;
|
||||
@@ -91,80 +74,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
set => SetProperty(ref _previewElements, value);
|
||||
}
|
||||
|
||||
// Visual editor properties
|
||||
public ObservableCollection<OledElement> OledElements
|
||||
{
|
||||
get => _oledElements;
|
||||
set => SetProperty(ref _oledElements, value);
|
||||
}
|
||||
|
||||
public OledElement SelectedElement
|
||||
{
|
||||
get => _selectedElement;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedElement, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(HasSelectedElement));
|
||||
OnPropertyChanged(nameof(IsTextElementSelected));
|
||||
OnPropertyChanged(nameof(IsBarElementSelected));
|
||||
OnPropertyChanged(nameof(IsRectangleElementSelected));
|
||||
OnPropertyChanged(nameof(IsLineElementSelected));
|
||||
OnPropertyChanged(nameof(IsIconElementSelected));
|
||||
|
||||
// Update element properties
|
||||
OnPropertyChanged(nameof(SelectedElementX));
|
||||
OnPropertyChanged(nameof(SelectedElementY));
|
||||
OnPropertyChanged(nameof(SelectedElementText));
|
||||
OnPropertyChanged(nameof(SelectedElementSize));
|
||||
OnPropertyChanged(nameof(SelectedElementWidth));
|
||||
OnPropertyChanged(nameof(SelectedElementHeight));
|
||||
OnPropertyChanged(nameof(SelectedElementValue));
|
||||
OnPropertyChanged(nameof(SelectedElementX2));
|
||||
OnPropertyChanged(nameof(SelectedElementY2));
|
||||
OnPropertyChanged(nameof(SelectedElementIconName));
|
||||
OnPropertyChanged(nameof(SelectedElementSensor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasSelectedElement => SelectedElement != null;
|
||||
public bool IsTextElementSelected => SelectedElement?.Type == "text";
|
||||
public bool IsBarElementSelected => SelectedElement?.Type == "bar";
|
||||
public bool IsRectangleElementSelected => SelectedElement?.Type == "rect" || SelectedElement?.Type == "box";
|
||||
public bool IsLineElementSelected => SelectedElement?.Type == "line";
|
||||
public bool IsIconElementSelected => SelectedElement?.Type == "icon";
|
||||
|
||||
public bool ShowGridLines
|
||||
{
|
||||
get => _showGridLines;
|
||||
set => SetProperty(ref _showGridLines, value);
|
||||
}
|
||||
|
||||
public float ZoomLevel
|
||||
{
|
||||
get => _zoomLevel;
|
||||
set => SetProperty(ref _zoomLevel, value);
|
||||
}
|
||||
|
||||
public string CurrentSensorFilter
|
||||
{
|
||||
get => _currentSensorFilter;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _currentSensorFilter, value))
|
||||
{
|
||||
ApplySensorFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<SensorItem> FilteredSensors
|
||||
{
|
||||
get => _filteredSensors;
|
||||
set => SetProperty(ref _filteredSensors, value);
|
||||
}
|
||||
|
||||
// Common properties
|
||||
public ObservableCollection<SensorItem> AvailableSensors
|
||||
{
|
||||
@@ -205,177 +114,11 @@ public class OledConfigViewModel : BaseViewModel
|
||||
set => SetProperty(ref _newTemplateName, value);
|
||||
}
|
||||
|
||||
// Selected element properties
|
||||
public string SelectedElementX
|
||||
{
|
||||
get => SelectedElement?.X.ToString() ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null && int.TryParse(value, out int x))
|
||||
{
|
||||
SelectedElement.X = x;
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementY
|
||||
{
|
||||
get => SelectedElement?.Y.ToString() ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null && int.TryParse(value, out int y))
|
||||
{
|
||||
SelectedElement.Y = y;
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementText
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("content") ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null)
|
||||
{
|
||||
SelectedElement.Properties["content"] = value;
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementSize
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("size") ?? "1";
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null)
|
||||
{
|
||||
SelectedElement.Properties["size"] = value;
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementWidth
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("width") ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null && int.TryParse(value, out int width))
|
||||
{
|
||||
SelectedElement.Properties["width"] = width.ToString();
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementWidth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementHeight
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("height") ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null && int.TryParse(value, out int height))
|
||||
{
|
||||
SelectedElement.Properties["height"] = height.ToString();
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementHeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float SelectedElementValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedElement != null && float.TryParse(SelectedElement.Properties.GetValueOrDefault("value"), out float value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null)
|
||||
{
|
||||
SelectedElement.Properties["value"] = value.ToString("F0");
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementX2
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("x2") ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null && int.TryParse(value, out int x2))
|
||||
{
|
||||
SelectedElement.Properties["x2"] = x2.ToString();
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementX2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementY2
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("y2") ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null && int.TryParse(value, out int y2))
|
||||
{
|
||||
SelectedElement.Properties["y2"] = y2.ToString();
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementY2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementIconName
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("name") ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null)
|
||||
{
|
||||
SelectedElement.Properties["name"] = value;
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementIconName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedElementSensor
|
||||
{
|
||||
get => SelectedElement?.Properties.GetValueOrDefault("sensor") ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SelectedElement != null)
|
||||
{
|
||||
SelectedElement.Properties["sensor"] = value;
|
||||
UpdateMarkupFromElements();
|
||||
OnPropertyChanged(nameof(SelectedElementSensor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lists for populating pickers
|
||||
public List<string> FontSizes => new List<string> { "1", "2", "3" };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
// Tab selection commands
|
||||
public ICommand SwitchToVisualEditorCommand { get; }
|
||||
public ICommand SwitchToMarkupEditorCommand { get; }
|
||||
public ICommand SwitchToTemplatesCommand { get; }
|
||||
|
||||
@@ -384,15 +127,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
public ICommand PreviewCommand { get; }
|
||||
public ICommand ResetCommand { get; }
|
||||
|
||||
// Visual editor commands
|
||||
public ICommand AddElementCommand { get; }
|
||||
public ICommand DeleteElementCommand { get; }
|
||||
public ICommand ZoomInCommand { get; }
|
||||
public ICommand ZoomOutCommand { get; }
|
||||
public ICommand FilterSensorsCommand { get; }
|
||||
public ICommand AddSensorToDisplayCommand { get; }
|
||||
public ICommand BrowseIconsCommand { get; }
|
||||
|
||||
// Markup editor commands
|
||||
public ICommand InsertMarkupCommand { get; }
|
||||
public ICommand InsertSensorVariableCommand { get; }
|
||||
@@ -416,10 +150,8 @@ public class OledConfigViewModel : BaseViewModel
|
||||
_serialPortService = serialPortService ?? throw new ArgumentNullException(nameof(serialPortService));
|
||||
|
||||
// Initialize collections
|
||||
_oledElements = new ObservableCollection<OledElement>();
|
||||
_previewElements = new List<PreviewElement>();
|
||||
_availableSensors = new ObservableCollection<SensorItem>();
|
||||
_filteredSensors = new ObservableCollection<SensorItem>();
|
||||
_templateList = new ObservableCollection<Template>();
|
||||
_customTemplates = new ObservableCollection<Template>();
|
||||
|
||||
@@ -427,19 +159,14 @@ public class OledConfigViewModel : BaseViewModel
|
||||
_sensorUpdateCts = new CancellationTokenSource();
|
||||
|
||||
// Create views
|
||||
_visualEditorView = new OledVisualEditorView { BindingContext = this };
|
||||
_markupEditorView = new OledMarkupEditorView { BindingContext = this };
|
||||
_templatesView = new OledTemplatesView { BindingContext = this };
|
||||
|
||||
// Default values
|
||||
_isVisualEditorSelected = true;
|
||||
_currentView = _visualEditorView;
|
||||
_showGridLines = false;
|
||||
_zoomLevel = 3.0f;
|
||||
_currentSensorFilter = "All";
|
||||
_isMarkupEditorSelected = true;
|
||||
_currentView = _markupEditorView;
|
||||
|
||||
// Tab selection commands
|
||||
SwitchToVisualEditorCommand = new Command(() => SwitchTab("visual"));
|
||||
SwitchToMarkupEditorCommand = new Command(() => SwitchTab("markup"));
|
||||
SwitchToTemplatesCommand = new Command(() => SwitchTab("templates"));
|
||||
|
||||
@@ -448,15 +175,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
PreviewCommand = new Command(async () => await PreviewOnDeviceAsync());
|
||||
ResetCommand = new Command(async () => await ResetLayoutAsync());
|
||||
|
||||
// Visual editor commands
|
||||
AddElementCommand = new Command<string>(type => AddElement(type));
|
||||
DeleteElementCommand = new Command(DeleteSelectedElement);
|
||||
ZoomInCommand = new Command(ZoomIn);
|
||||
ZoomOutCommand = new Command(ZoomOut);
|
||||
FilterSensorsCommand = new Command<string>(filter => CurrentSensorFilter = filter);
|
||||
AddSensorToDisplayCommand = new Command<string>(sensorId => AddSensorToDisplay(sensorId));
|
||||
BrowseIconsCommand = new Command(async () => await BrowseIconsAsync());
|
||||
|
||||
// Markup editor commands
|
||||
InsertMarkupCommand = new Command<string>(type => InsertMarkupTemplate(type));
|
||||
InsertSensorVariableCommand = new Command(async () => await InsertSensorVariableAsync());
|
||||
@@ -507,8 +225,8 @@ public class OledConfigViewModel : BaseViewModel
|
||||
// Load templates
|
||||
await LoadTemplatesAsync();
|
||||
|
||||
// Switch to visual editor by default
|
||||
SwitchTab("visual");
|
||||
// Switch to markup editor by default
|
||||
SwitchTab("markup");
|
||||
|
||||
// Start sensor updates
|
||||
_sensorUpdateTimer.Change(0, 2000); // Update every 2 seconds
|
||||
@@ -544,7 +262,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
// Update on main thread to ensure thread safety
|
||||
await MainThread.InvokeOnMainThreadAsync(() => {
|
||||
AvailableSensors = new ObservableCollection<SensorItem>(sensors);
|
||||
FilteredSensors = new ObservableCollection<SensorItem>(sensors);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -563,7 +280,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
if (!string.IsNullOrEmpty(config.OledMarkup))
|
||||
{
|
||||
OledMarkup = config.OledMarkup;
|
||||
await ParseMarkupToElementsAsync(OledMarkup);
|
||||
UpdatePreviewFromMarkup();
|
||||
}
|
||||
else
|
||||
@@ -653,19 +369,14 @@ public class OledConfigViewModel : BaseViewModel
|
||||
|
||||
private void SwitchTab(string tab)
|
||||
{
|
||||
IsVisualEditorSelected = tab == "visual";
|
||||
IsMarkupEditorSelected = tab == "markup";
|
||||
IsTemplatesSelected = tab == "templates";
|
||||
|
||||
OnPropertyChanged(nameof(IsVisualEditorSelected));
|
||||
OnPropertyChanged(nameof(IsMarkupEditorSelected));
|
||||
OnPropertyChanged(nameof(IsTemplatesSelected));
|
||||
|
||||
switch (tab)
|
||||
{
|
||||
case "visual":
|
||||
CurrentView = _visualEditorView;
|
||||
break;
|
||||
case "markup":
|
||||
CurrentView = _markupEditorView;
|
||||
break;
|
||||
@@ -784,26 +495,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMarkupFromElements()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
|
||||
foreach (var element in OledElements)
|
||||
{
|
||||
sb.AppendLine(element.ToMarkup());
|
||||
}
|
||||
|
||||
OledMarkup = sb.ToString();
|
||||
UpdatePreviewFromMarkup();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error updating markup from elements: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateSensorDataAsync()
|
||||
{
|
||||
try
|
||||
@@ -829,317 +520,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySensorFilter()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(CurrentSensorFilter) || CurrentSensorFilter == "All")
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
FilteredSensors = new ObservableCollection<SensorItem>(AvailableSensors);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var filtered = AvailableSensors.Where(s =>
|
||||
s.HardwareName.Contains(CurrentSensorFilter, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
FilteredSensors = new ObservableCollection<SensorItem>(filtered);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error applying sensor filter: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AddElement(string type)
|
||||
{
|
||||
try
|
||||
{
|
||||
var element = new OledElement
|
||||
{
|
||||
Type = type,
|
||||
X = 10,
|
||||
Y = 10
|
||||
};
|
||||
|
||||
// Set default properties based on type
|
||||
switch (type)
|
||||
{
|
||||
case "text":
|
||||
element.Properties["size"] = "1";
|
||||
element.Properties["content"] = "New Text";
|
||||
break;
|
||||
|
||||
case "bar":
|
||||
element.Properties["width"] = "100";
|
||||
element.Properties["height"] = "8";
|
||||
element.Properties["value"] = "50";
|
||||
break;
|
||||
|
||||
case "rect":
|
||||
case "box":
|
||||
element.Properties["width"] = "20";
|
||||
element.Properties["height"] = "10";
|
||||
break;
|
||||
|
||||
case "line":
|
||||
element.Properties["x2"] = "30";
|
||||
element.Properties["y2"] = "30";
|
||||
break;
|
||||
|
||||
case "icon":
|
||||
element.Properties["name"] = "cpu";
|
||||
break;
|
||||
}
|
||||
|
||||
OledElements.Add(element);
|
||||
SelectedElement = element;
|
||||
|
||||
// Update markup
|
||||
UpdateMarkupFromElements();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error adding element: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteSelectedElement()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (SelectedElement != null)
|
||||
{
|
||||
OledElements.Remove(SelectedElement);
|
||||
SelectedElement = null;
|
||||
|
||||
// Update markup
|
||||
UpdateMarkupFromElements();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error deleting element: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ParseMarkupToElementsAsync(string markup)
|
||||
{
|
||||
if (string.IsNullOrEmpty(markup))
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(() => {
|
||||
OledElements.Clear();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var elements = new List<OledElement>();
|
||||
|
||||
try
|
||||
{
|
||||
// Parse text elements
|
||||
foreach (Match match in Regex.Matches(markup, @"<text\s+x=(\d+)\s+y=(\d+)(?:\s+size=(\d+))?>([^<]*)</text>"))
|
||||
{
|
||||
var element = new OledElement { Type = "text" };
|
||||
element.X = int.Parse(match.Groups[1].Value);
|
||||
element.Y = int.Parse(match.Groups[2].Value);
|
||||
|
||||
if (match.Groups[3].Success)
|
||||
{
|
||||
element.Properties["size"] = match.Groups[3].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
element.Properties["size"] = "1";
|
||||
}
|
||||
|
||||
element.Properties["content"] = match.Groups[4].Value;
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// Parse bar elements
|
||||
foreach (Match match in Regex.Matches(markup, @"<bar\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s+val=(\d+|\{[^}]+\})\s*/>"))
|
||||
{
|
||||
var element = new OledElement { Type = "bar" };
|
||||
element.X = int.Parse(match.Groups[1].Value);
|
||||
element.Y = int.Parse(match.Groups[2].Value);
|
||||
element.Properties["width"] = match.Groups[3].Value;
|
||||
element.Properties["height"] = match.Groups[4].Value;
|
||||
element.Properties["value"] = match.Groups[5].Value;
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// Parse rect elements
|
||||
foreach (Match match in Regex.Matches(markup, @"<rect\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
|
||||
{
|
||||
var element = new OledElement { Type = "rect" };
|
||||
element.X = int.Parse(match.Groups[1].Value);
|
||||
element.Y = int.Parse(match.Groups[2].Value);
|
||||
element.Properties["width"] = match.Groups[3].Value;
|
||||
element.Properties["height"] = match.Groups[4].Value;
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// Parse box elements
|
||||
foreach (Match match in Regex.Matches(markup, @"<box\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
|
||||
{
|
||||
var element = new OledElement { Type = "box" };
|
||||
element.X = int.Parse(match.Groups[1].Value);
|
||||
element.Y = int.Parse(match.Groups[2].Value);
|
||||
element.Properties["width"] = match.Groups[3].Value;
|
||||
element.Properties["height"] = match.Groups[4].Value;
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// Parse line elements
|
||||
foreach (Match match in Regex.Matches(markup, @"<line\s+x1=(\d+)\s+y1=(\d+)\s+x2=(\d+)\s+y2=(\d+)\s*/>"))
|
||||
{
|
||||
var element = new OledElement { Type = "line" };
|
||||
element.X = int.Parse(match.Groups[1].Value);
|
||||
element.Y = int.Parse(match.Groups[2].Value);
|
||||
element.Properties["x2"] = match.Groups[3].Value;
|
||||
element.Properties["y2"] = match.Groups[4].Value;
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// Parse icon elements
|
||||
foreach (Match match in Regex.Matches(markup, @"<icon\s+x=(\d+)\s+y=(\d+)\s+name=([a-zA-Z0-9_]+)\s*/>"))
|
||||
{
|
||||
var element = new OledElement { Type = "icon" };
|
||||
element.X = int.Parse(match.Groups[1].Value);
|
||||
element.Y = int.Parse(match.Groups[2].Value);
|
||||
element.Properties["name"] = match.Groups[3].Value;
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// Update the collection on the UI thread
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
OledElements.Clear();
|
||||
foreach (var element in elements)
|
||||
{
|
||||
OledElements.Add(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error parsing markup: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<PreviewElement>> ParseMarkupToPreviewElements(string markup)
|
||||
{
|
||||
try
|
||||
{
|
||||
var markupParser = new MarkupParser(_sensorService.GetAllSensorValues());
|
||||
return markupParser.ParseMarkup(markup);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error parsing markup for preview: {ex.Message}");
|
||||
return new List<PreviewElement>();
|
||||
}
|
||||
}
|
||||
|
||||
private void ZoomIn()
|
||||
{
|
||||
if (ZoomLevel < 5.0f)
|
||||
{
|
||||
ZoomLevel += 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private void ZoomOut()
|
||||
{
|
||||
if (ZoomLevel > 1.0f)
|
||||
{
|
||||
ZoomLevel -= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSensorToDisplay(string sensorId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Find the sensor
|
||||
var sensor = AvailableSensors.FirstOrDefault(s => s.Id == sensorId);
|
||||
|
||||
if (sensor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine appropriate Y position (avoid overlap)
|
||||
int yPos = 15;
|
||||
if (OledElements.Any())
|
||||
{
|
||||
yPos = OledElements.Max(e => e.Y) + 15;
|
||||
}
|
||||
|
||||
// Create text element with the sensor variable
|
||||
var element = new OledElement
|
||||
{
|
||||
Type = "text",
|
||||
X = 10,
|
||||
Y = yPos
|
||||
};
|
||||
|
||||
element.Properties["size"] = "1";
|
||||
element.Properties["content"] = $"{sensor.Name}: {{{sensorId}}} {sensor.Unit}";
|
||||
|
||||
OledElements.Add(element);
|
||||
SelectedElement = element;
|
||||
|
||||
// Create a progress bar if it's a load/percentage sensor
|
||||
if (sensor.SensorType == LibreHardwareMonitor.Hardware.SensorType.Load ||
|
||||
sensor.SensorType == LibreHardwareMonitor.Hardware.SensorType.Level)
|
||||
{
|
||||
var barElement = new OledElement
|
||||
{
|
||||
Type = "bar",
|
||||
X = 10,
|
||||
Y = yPos + 5
|
||||
};
|
||||
|
||||
barElement.Properties["width"] = "100";
|
||||
barElement.Properties["height"] = "8";
|
||||
barElement.Properties["value"] = $"{{{sensorId}}}";
|
||||
|
||||
OledElements.Add(barElement);
|
||||
}
|
||||
|
||||
// Update markup
|
||||
UpdateMarkupFromElements();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error adding sensor to display: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseIconsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// A mock implementation - in a real app, you'd implement a proper icon browser
|
||||
var icons = new string[] { "cpu", "gpu", "ram", "disk", "network", "fan" };
|
||||
string result = await Shell.Current.DisplayActionSheet("Select an icon", "Cancel", null, icons);
|
||||
|
||||
if (!string.IsNullOrEmpty(result) && result != "Cancel")
|
||||
{
|
||||
SelectedElementIconName = result;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error browsing icons: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertMarkupTemplate(string type)
|
||||
{
|
||||
try
|
||||
@@ -1216,7 +596,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
try
|
||||
{
|
||||
OledMarkup = await _sensorService.CreateExampleMarkupAsync();
|
||||
await ParseMarkupToElementsAsync(OledMarkup);
|
||||
UpdatePreviewFromMarkup();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1225,6 +604,20 @@ public class OledConfigViewModel : BaseViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<PreviewElement>> ParseMarkupToPreviewElements(string markup)
|
||||
{
|
||||
try
|
||||
{
|
||||
var markupParser = new MarkupParser(_sensorService.GetAllSensorValues());
|
||||
return markupParser.ParseMarkup(markup);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error parsing markup for preview: {ex.Message}");
|
||||
return new List<PreviewElement>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> CreateSystemMonitorTemplateAsync()
|
||||
{
|
||||
try
|
||||
@@ -1324,7 +717,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
if (confirm)
|
||||
{
|
||||
OledMarkup = SelectedTemplate.Markup;
|
||||
await ParseMarkupToElementsAsync(OledMarkup);
|
||||
UpdatePreviewFromMarkup();
|
||||
SwitchTab("markup");
|
||||
}
|
||||
@@ -1424,7 +816,6 @@ public class OledConfigViewModel : BaseViewModel
|
||||
if (confirm)
|
||||
{
|
||||
OledMarkup = template.Markup;
|
||||
await ParseMarkupToElementsAsync(OledMarkup);
|
||||
UpdatePreviewFromMarkup();
|
||||
SwitchTab("markup");
|
||||
}
|
||||
@@ -1472,9 +863,15 @@ public class OledConfigViewModel : BaseViewModel
|
||||
|
||||
public class Template
|
||||
{
|
||||
public Template()
|
||||
{
|
||||
// Initialize the PreviewElements collection
|
||||
PreviewElements = new List<PreviewElement>();
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Markup { get; set; }
|
||||
public List<PreviewElement> PreviewElements { get; set; } = new List<PreviewElement>();
|
||||
public List<PreviewElement> PreviewElements { get; set; }
|
||||
public bool IsSelected { get; set; }
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
FontSize="22"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{StaticResource TextPrimary}" />
|
||||
<Label Text="Design your custom OLED display layout with the visual editor or markup"
|
||||
<Label Text="Design your custom OLED display layout with markup"
|
||||
FontSize="14"
|
||||
TextColor="{StaticResource TextSecondary}" />
|
||||
<BoxView HeightRequest="1" Color="{StaticResource BorderColor}" Margin="0,10,0,0" />
|
||||
@@ -22,20 +22,6 @@
|
||||
|
||||
<!-- Tabs -->
|
||||
<HorizontalStackLayout Grid.Row="1" Spacing="0">
|
||||
<Border BackgroundColor="{Binding IsVisualEditorSelected, Converter={StaticResource BoolToColorConverter}, ConverterParameter={StaticResource Primary}}"
|
||||
StrokeShape="RoundRectangle 4,4,0,0"
|
||||
StrokeThickness="1"
|
||||
Stroke="{StaticResource BorderColor}"
|
||||
Padding="20,10"
|
||||
WidthRequest="160">
|
||||
<Label Text="Visual Editor"
|
||||
TextColor="{Binding IsVisualEditorSelected, Converter={StaticResource BoolToTextColorConverter}, ConverterParameter=White}"
|
||||
HorizontalOptions="Center" />
|
||||
<Border.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding SwitchToVisualEditorCommand}" />
|
||||
</Border.GestureRecognizers>
|
||||
</Border>
|
||||
|
||||
<Border BackgroundColor="{Binding IsMarkupEditorSelected, Converter={StaticResource BoolToColorConverter}, ConverterParameter={StaticResource Primary}}"
|
||||
StrokeShape="RoundRectangle 4,4,0,0"
|
||||
StrokeThickness="1"
|
||||
|
||||
@@ -34,13 +34,12 @@
|
||||
FontAttributes="Bold" />
|
||||
|
||||
<Grid Grid.Row="1" BackgroundColor="Black" Padding="10">
|
||||
<controls:OledPreviewCanvas Elements="{Binding PreviewElements}"
|
||||
Width="256"
|
||||
Height="64"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Scale="2"
|
||||
IsEditable="False" />
|
||||
<controls:OledDisplayCanvas Elements="{Binding PreviewElements}"
|
||||
CanvasWidth="256"
|
||||
CanvasHeight="64"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Scale="2" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -34,13 +34,12 @@
|
||||
Margin="0,0,0,10" />
|
||||
|
||||
<Grid Grid.Row="1" BackgroundColor="Black" HeightRequest="100">
|
||||
<controls:OledPreviewCanvas Elements="{Binding PreviewElements}"
|
||||
Width="256"
|
||||
Height="64"
|
||||
<controls:OledDisplayCanvas Elements="{Binding PreviewElements}"
|
||||
CanvasWidth="256"
|
||||
CanvasHeight="64"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Scale="1.5"
|
||||
IsEditable="False" />
|
||||
Scale="1.5" />
|
||||
</Grid>
|
||||
|
||||
<Label Grid.Row="2"
|
||||
|
||||
Reference in New Issue
Block a user