Add constant rich text placeholders
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
2026-08-30 23:23:48 +02:00
parent 28469bf547
commit 527c186090
15 changed files with 529 additions and 15 deletions
@@ -109,6 +109,8 @@ public partial class DesignerViewModel : ObservableObject
issues.Add(new(ValidationSeverity.Error, $"Platzhalter „{used}“ ist nicht deklariert."));
foreach (var path in layout.Elements.Select(x => x switch { BackgroundElement b => b.Path, ImageElement i => i.Path, _ => null }).Where(x => x is not null))
if (!Assets.ContainsKey(path!)) issues.Add(new(ValidationSeverity.Error, $"Asset „{path}“ fehlt."));
foreach (var issue in TemplateDataResolver.ValidateConstants(manifest))
issues.Add(new(ValidationSeverity.Error, issue));
if (issues.Count > 0) throw new TemplateValidationException(new(issues));
return new(manifest, layout, new Dictionary<string, byte[]>(Assets));
}
@@ -145,7 +147,9 @@ public partial class DesignerViewModel : ObservableObject
SelectedMetadata = null;
Placeholders.Clear();
foreach (var placeholder in template.Manifest.Placeholders)
Placeholders.Add(new(placeholder.Name, placeholder.Type, placeholder.Required, DesignerPlaceholder.SampleFor(placeholder.Type)));
Placeholders.Add(new(placeholder.Name, placeholder.Type, placeholder.Required,
placeholder.IsConstant ? placeholder.ConstantValue ?? "" : DesignerPlaceholder.SampleFor(placeholder.Type),
placeholder.IsConstant, placeholder.Bold, placeholder.Italic, placeholder.Underline));
SelectedPlaceholder = Placeholders.FirstOrDefault();
Assets.Clear(); AssetItems.Clear();
foreach (var asset in template.Assets) AddOrReplaceAsset(asset.Key, asset.Value, keepName: true);
@@ -201,7 +205,9 @@ public partial class DesignerViewModel : ObservableObject
var name = placeholder.Name.Trim();
if (name.Length == 0) throw new InvalidDataException("Ein Platzhaltername darf nicht leer sein.");
if (!names.Add(name)) throw new InvalidDataException($"Platzhalter „{name}“ ist mehrfach definiert.");
result.Add(new(name, placeholder.Type, placeholder.Required));
result.Add(new(name, placeholder.Type, placeholder.Required, placeholder.IsConstant,
placeholder.IsConstant ? placeholder.Sample : null,
placeholder.Bold, placeholder.Italic, placeholder.Underline));
}
return result;
}
@@ -439,8 +445,29 @@ public partial class DesignerPlaceholder : ObservableObject
[ObservableProperty] private PlaceholderType _type;
[ObservableProperty] private bool _required;
[ObservableProperty] private string _sample;
public DesignerPlaceholder(string name, PlaceholderType type, bool required, string sample)
{ _name = name; _type = type; _required = required; _sample = sample; }
[ObservableProperty] private bool _isConstant;
[ObservableProperty] private bool _bold;
[ObservableProperty] private bool _italic;
[ObservableProperty] private bool _underline;
public bool SupportsConstantValue => Type is PlaceholderType.Text or PlaceholderType.Multiline
or PlaceholderType.Date or PlaceholderType.Number;
public bool SupportsRichText => IsConstant && Type is PlaceholderType.Text or PlaceholderType.Multiline;
public DesignerPlaceholder(string name, PlaceholderType type, bool required, string sample,
bool isConstant = false, bool bold = false, bool italic = false, bool underline = false)
{
_name = name; _type = type; _required = required; _sample = sample;
_isConstant = isConstant; _bold = bold; _italic = italic; _underline = underline;
}
partial void OnTypeChanged(PlaceholderType value)
{
OnPropertyChanged(nameof(SupportsConstantValue));
OnPropertyChanged(nameof(SupportsRichText));
if (!SupportsConstantValue) IsConstant = false;
}
partial void OnIsConstantChanged(bool value) => OnPropertyChanged(nameof(SupportsRichText));
public PlaceholderValue ToValue() => Type switch
{
+26 -2
View File
@@ -113,9 +113,33 @@
<ComboBox ItemsSource="{Binding PlaceholderTypes}" SelectedItem="{Binding SelectedPlaceholder.Type, Mode=TwoWay}"/></StackPanel>
</Grid>
<CheckBox Content="Pflichtwert" IsChecked="{Binding SelectedPlaceholder.Required, Mode=TwoWay}"/>
<StackPanel><TextBlock Text="Beispielwert für die Vorschau" Classes="label"/>
<TextBox Text="{Binding SelectedPlaceholder.Sample, Mode=TwoWay}" AcceptsReturn="True"
<CheckBox IsChecked="{Binding SelectedPlaceholder.IsConstant, Mode=TwoWay}"
IsEnabled="{Binding SelectedPlaceholder.SupportsConstantValue}">
<TextBlock Text="Beispielwert fest im Paket verwenden (konstant, extern nicht überschreibbar)"
TextWrapping="Wrap"/>
</CheckBox>
<StackPanel><TextBlock Text="Beispielwert / konstanter Paketwert" Classes="label"/>
<StackPanel Orientation="Horizontal" Spacing="6" Margin="0,3,0,5">
<Button Content="F" FontWeight="Bold" Padding="10,3" Click="OnBoldSelection"
IsEnabled="{Binding SelectedPlaceholder.SupportsRichText}" ToolTip.Tip="Markierten Text fett setzen"/>
<Button Content="K" FontStyle="Italic" Padding="10,3" Click="OnItalicSelection"
IsEnabled="{Binding SelectedPlaceholder.SupportsRichText}" ToolTip.Tip="Markierten Text kursiv setzen"/>
<Button Content="U̲" Padding="10,3" Click="OnUnderlineSelection"
IsEnabled="{Binding SelectedPlaceholder.SupportsRichText}" ToolTip.Tip="Markierten Text unterstreichen"/>
</StackPanel>
<TextBox x:Name="PlaceholderValueTextBox" Text="{Binding SelectedPlaceholder.Sample, Mode=TwoWay}" AcceptsReturn="True"
MinHeight="68" MaxHeight="120" TextWrapping="Wrap" PlaceholderText="Beispieldaten eingeben"/></StackPanel>
<TextBlock IsVisible="{Binding SelectedPlaceholder.SupportsRichText}"
Text="Externe Werte im Text: ${Student.LastName}. Markup: [b]fett[/b], [i]kursiv[/i], [u]unterstrichen[/u]."
FontSize="11" Opacity="0.65" TextWrapping="Wrap"/>
<StackPanel>
<TextBlock Text="Hervorhebung bei direkter Verwendung in TEXT/TEXTBOX" Classes="label"/>
<StackPanel Orientation="Horizontal" Spacing="14">
<CheckBox Content="Fett" IsChecked="{Binding SelectedPlaceholder.Bold, Mode=TwoWay}"/>
<CheckBox Content="Kursiv" IsChecked="{Binding SelectedPlaceholder.Italic, Mode=TwoWay}"/>
<CheckBox Content="Unterstrichen" IsChecked="{Binding SelectedPlaceholder.Underline, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
<Button Content="Platzhalter entfernen" HorizontalAlignment="Left" Click="OnRemovePlaceholder"/>
</StackPanel>
</Border>
@@ -39,6 +39,23 @@ public partial class MainWindow : Window
_viewModel.MetadataItems.Remove(selected); _viewModel.SelectedMetadata = null; _viewModel.CanExport = false;
}
private void OnRemovePlaceholder(object? sender, RoutedEventArgs e) => _viewModel.RemoveSelectedPlaceholder();
private void OnBoldSelection(object? sender, RoutedEventArgs e) => WrapPlaceholderSelection("[b]", "[/b]");
private void OnItalicSelection(object? sender, RoutedEventArgs e) => WrapPlaceholderSelection("[i]", "[/i]");
private void OnUnderlineSelection(object? sender, RoutedEventArgs e) => WrapPlaceholderSelection("[u]", "[/u]");
private void WrapPlaceholderSelection(string opening, string closing)
{
if (_viewModel.SelectedPlaceholder is not { SupportsRichText: true } placeholder
|| this.FindControl<TextBox>("PlaceholderValueTextBox") is not { } editor) return;
var source = editor.Text ?? "";
var start = Math.Min(editor.SelectionStart, editor.SelectionEnd);
var end = Math.Max(editor.SelectionStart, editor.SelectionEnd);
var updated = source[..start] + opening + source[start..end] + closing + source[end..];
placeholder.Sample = updated; editor.Text = updated;
editor.SelectionStart = start + opening.Length;
editor.SelectionEnd = end + opening.Length;
editor.Focus(); _viewModel.CanExport = false;
}
private void OnAddElement(object? sender, RoutedEventArgs e)
{ try { _viewModel.AddElement(); } catch (Exception ex) { _viewModel.SetStatus(ex.Message, true); } }
private void OnMeasureOverlayMode(object? sender, RoutedEventArgs e)
@@ -110,7 +110,8 @@ public sealed class StarterTemplateLibrary
LayoutFile = source.LayoutFile,
MetadataFile = source.MetadataFile,
Metadata = new(source.Metadata, StringComparer.OrdinalIgnoreCase),
Placeholders = source.Placeholders.Select(x => new PlaceholderDefinition(x.Name, x.Type, x.Required)).ToList(),
Placeholders = source.Placeholders.Select(x => new PlaceholderDefinition(x.Name, x.Type, x.Required,
x.IsConstant, x.ConstantValue, x.Bold, x.Italic, x.Underline)).ToList(),
};
private static StarterTemplateItem ToItem(TemplateManifest manifest, string path) =>