Add visual template coordinate editor
This commit is contained in:
@@ -30,6 +30,14 @@ public partial class DesignerViewModel : ObservableObject
|
||||
[ObservableProperty] private DesignerPlaceholder? _selectedPlaceholder;
|
||||
[ObservableProperty] private DesignerAsset? _selectedAsset;
|
||||
[ObservableProperty] private StarterTemplateItem? _selectedStarterTemplate;
|
||||
[ObservableProperty] private OverlayEditorMode _overlayMode = OverlayEditorMode.Measure;
|
||||
[ObservableProperty] private bool _snapOverlayToGrid = true;
|
||||
[ObservableProperty] private double _overlayGridSize = 1;
|
||||
[ObservableProperty] private string _overlayCoordinates = "x=– · y=–";
|
||||
[ObservableProperty] private string _selectedOverlayElement = "Kein Element ausgewählt";
|
||||
[ObservableProperty] private double _overlayPageWidth = 210;
|
||||
[ObservableProperty] private double _overlayPageHeight = 297;
|
||||
[ObservableProperty] private string _overlayPageUnit = "mm";
|
||||
|
||||
public IReadOnlyList<string> Units { get; } = ["mm", "cm", "pt", "in"];
|
||||
public IReadOnlyList<PlaceholderType> PlaceholderTypes { get; } = Enum.GetValues<PlaceholderType>();
|
||||
@@ -190,7 +198,54 @@ public partial class DesignerViewModel : ObservableObject
|
||||
: $"Asset „{selected.Name}“ und {removedReferences} Layout-Referenz(en) wurden entfernt.", false);
|
||||
}
|
||||
|
||||
partial void OnLayoutSourceChanged(string value) => RefreshAssetUsage();
|
||||
public void ApplyMeasurement(OverlayMeasurement measurement)
|
||||
{
|
||||
NewX = FormatNumber(measurement.X); NewY = FormatNumber(measurement.Y);
|
||||
if (measurement.HasArea)
|
||||
{
|
||||
NewWidth = FormatNumber(measurement.Width); NewHeight = FormatNumber(measurement.Height);
|
||||
SetStatus($"Bereich übernommen: x={NewX}, y={NewY}, b={NewWidth}, h={NewHeight} {OverlayPageUnit}.", false);
|
||||
}
|
||||
else SetStatus($"Koordinate übernommen: x={NewX}, y={NewY} {OverlayPageUnit}.", false);
|
||||
}
|
||||
|
||||
public void ApplyElementGeometry(OverlayElementGeometry geometry)
|
||||
{
|
||||
var layout = new LayoutParser().Parse(LayoutSource);
|
||||
var element = layout.Elements.FirstOrDefault(x => x.Line == geometry.Line)
|
||||
?? throw new InvalidDataException($"Element in Zeile {geometry.Line} wurde nicht gefunden.");
|
||||
var lines = LayoutSource.Replace("\r\n", "\n", StringComparison.Ordinal).Split('\n').ToList();
|
||||
var index = geometry.Line - 1;
|
||||
if (index < 0 || index >= lines.Count) throw new InvalidDataException("Elementzeile liegt außerhalb des Layouts.");
|
||||
var x = FormatNumber(geometry.X); var y = FormatNumber(geometry.Y);
|
||||
var width = geometry.Width; var height = geometry.Height;
|
||||
lines[index] = element switch
|
||||
{
|
||||
ImageElement image => BuildImageLine(image, x, y, width, height),
|
||||
TextElement text => $"TEXT {x} {y} {Content(text.Content, text.Placeholder, text.Format)}{Attributes(text.Attributes)}",
|
||||
TextBoxElement box => $"TEXTBOX {x} {y} {FormatNumber(width)} {FormatNumber(height)} "
|
||||
+ $"{Content(box.Content, box.Placeholder, box.Format)}{Attributes(box.Attributes)}",
|
||||
TableElement table => $"TABLE {x} {y} {FormatNumber(width)} {FormatNumber(height)} "
|
||||
+ $"${table.Placeholder}{Attributes(table.Attributes)}",
|
||||
ChartElement chart => $"CHART {x} {y} {FormatNumber(width)} {FormatNumber(height)} "
|
||||
+ $"${chart.Placeholder}{Attributes(chart.Attributes)}",
|
||||
_ => lines[index],
|
||||
};
|
||||
LayoutSource = string.Join('\n', lines); CanExport = false;
|
||||
SelectedOverlayElement = $"{geometry.Keyword} · Zeile {geometry.Line}";
|
||||
SetStatus($"{geometry.Keyword} verschoben/skalisiert. PDF-Vorschau wird aktualisiert.", false);
|
||||
}
|
||||
|
||||
partial void OnLayoutSourceChanged(string value)
|
||||
{
|
||||
RefreshAssetUsage();
|
||||
try
|
||||
{
|
||||
var page = new LayoutParser().Parse(value);
|
||||
OverlayPageWidth = page.Width; OverlayPageHeight = page.Height; OverlayPageUnit = page.Unit;
|
||||
}
|
||||
catch (TemplateValidationException) { }
|
||||
}
|
||||
|
||||
private DesignerAsset AddOrReplaceAsset(string sourceName, byte[] bytes, bool keepName)
|
||||
{
|
||||
@@ -253,6 +308,19 @@ public partial class DesignerViewModel : ObservableObject
|
||||
return value.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private static string BuildImageLine(ImageElement image, string x, string y, double effectiveWidth, double effectiveHeight)
|
||||
{
|
||||
var scale = image.Attributes.TryGetValue("scale", out var raw) ? LayoutParser.Percentage(raw) : 1f;
|
||||
return $"IMG {image.Path} {x} {y} {FormatNumber(effectiveWidth / scale)} "
|
||||
+ $"{FormatNumber(effectiveHeight / scale)}{Attributes(image.Attributes)}";
|
||||
}
|
||||
private static string Content(string original, string? placeholder, string? format) => placeholder is null
|
||||
? $"\"{original.Replace("\"", "\\\"")}\""
|
||||
: $"${placeholder}{(format is null ? "" : "|" + format)}";
|
||||
private static string Attributes(IReadOnlyDictionary<string, string> attributes) => attributes.Count == 0
|
||||
? "" : " " + string.Join(' ', attributes.Select(x => $"{x.Key}={x.Value}"));
|
||||
private static string FormatNumber(double value) => value.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
|
||||
public void SetStatus(string text, bool error)
|
||||
{ Status = text; StatusColor = error ? "#B91C1C" : "#166534"; }
|
||||
private static string QuoteIfLiteral(string value) => value.StartsWith('$') ? value : $"\"{value.Replace("\"", "\\\"")}\"";
|
||||
|
||||
Reference in New Issue
Block a user