This commit is contained in:
@@ -42,6 +42,42 @@ public sealed class ProjectLifecycleTests
|
|||||||
Assert.Equal("2026/27", manifest.Metadata["school-year"]);
|
Assert.Equal("2026/27", manifest.Metadata["school-year"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PlatzhalterAnlegen_WaehltIhnAusUndUebernimmtBearbeiteteDefinition()
|
||||||
|
{
|
||||||
|
var viewModel = new DesignerViewModel();
|
||||||
|
viewModel.Placeholders.Clear();
|
||||||
|
|
||||||
|
var placeholder = viewModel.AddPlaceholder();
|
||||||
|
placeholder.Name = "Sprache";
|
||||||
|
placeholder.Type = LehrerApp.Templating.PlaceholderType.Text;
|
||||||
|
placeholder.Required = true;
|
||||||
|
placeholder.Sample = "Deutsch";
|
||||||
|
|
||||||
|
Assert.Same(placeholder, viewModel.SelectedPlaceholder);
|
||||||
|
var definition = Assert.Single(viewModel.BuildManifest().Placeholders);
|
||||||
|
Assert.Equal("Sprache", definition.Name);
|
||||||
|
Assert.Equal(LehrerApp.Templating.PlaceholderType.Text, definition.Type);
|
||||||
|
Assert.True(definition.Required);
|
||||||
|
|
||||||
|
viewModel.RemoveSelectedPlaceholder();
|
||||||
|
Assert.Empty(viewModel.Placeholders);
|
||||||
|
Assert.Null(viewModel.SelectedPlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DoppeltePlatzhalternamen_WerdenVerstaendlichAbgelehnt()
|
||||||
|
{
|
||||||
|
var viewModel = new DesignerViewModel();
|
||||||
|
viewModel.Placeholders.Clear();
|
||||||
|
viewModel.Placeholders.Add(new("Name", LehrerApp.Templating.PlaceholderType.Text, false, "A"));
|
||||||
|
viewModel.Placeholders.Add(new("Name", LehrerApp.Templating.PlaceholderType.Text, false, "B"));
|
||||||
|
|
||||||
|
var exception = Assert.Throws<InvalidDataException>(() => viewModel.BuildManifest());
|
||||||
|
|
||||||
|
Assert.Contains("mehrfach", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly byte[] OnePixelPng = Convert.FromBase64String(
|
private static readonly byte[] OnePixelPng = Convert.FromBase64String(
|
||||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=");
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
public ObservableCollection<DesignerAsset> AssetItems { get; } = [];
|
public ObservableCollection<DesignerAsset> AssetItems { get; } = [];
|
||||||
public ObservableCollection<StarterTemplateItem> StarterTemplates { get; } = [];
|
public ObservableCollection<StarterTemplateItem> StarterTemplates { get; } = [];
|
||||||
|
|
||||||
|
public DesignerViewModel() => SelectedPlaceholder = Placeholders.FirstOrDefault();
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
TemplateId = "neue-vorlage"; TemplateName = "Neue Vorlage"; Description = "";
|
TemplateId = "neue-vorlage"; TemplateName = "Neue Vorlage"; Description = "";
|
||||||
@@ -83,7 +85,7 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
Id = TemplateId.Trim(), Name = TemplateName.Trim(), Description = Description.Trim(),
|
Id = TemplateId.Trim(), Name = TemplateName.Trim(), Description = Description.Trim(),
|
||||||
PageSize = new((float)PageWidth, (float)PageHeight, Unit), LayoutFile = "layout.tpl",
|
PageSize = new((float)PageWidth, (float)PageHeight, Unit), LayoutFile = "layout.tpl",
|
||||||
Metadata = BuildMetadata(),
|
Metadata = BuildMetadata(),
|
||||||
Placeholders = Placeholders.Select(x => new PlaceholderDefinition(x.Name.Trim(), x.Type, x.Required)).ToList(),
|
Placeholders = BuildPlaceholders(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public LoadedTemplate BuildLoaded()
|
public LoadedTemplate BuildLoaded()
|
||||||
@@ -106,6 +108,24 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
public ITemplateDataProvider BuildDataProvider() => new DesignerDataProvider(Placeholders.ToDictionary(
|
public ITemplateDataProvider BuildDataProvider() => new DesignerDataProvider(Placeholders.ToDictionary(
|
||||||
x => x.Name.Trim(), x => x.ToValue(), StringComparer.Ordinal));
|
x => x.Name.Trim(), x => x.ToValue(), StringComparer.Ordinal));
|
||||||
|
|
||||||
|
public DesignerPlaceholder AddPlaceholder()
|
||||||
|
{
|
||||||
|
var existing = Placeholders.Select(x => x.Name).ToHashSet(StringComparer.Ordinal);
|
||||||
|
var index = Placeholders.Count + 1;
|
||||||
|
while (existing.Contains($"Feld{index}")) index++;
|
||||||
|
var placeholder = new DesignerPlaceholder($"Feld{index}", PlaceholderType.Text, false, "Beispiel");
|
||||||
|
Placeholders.Add(placeholder); SelectedPlaceholder = placeholder; CanExport = false;
|
||||||
|
SetStatus($"Platzhalter „{placeholder.Name}“ angelegt. Details können jetzt bearbeitet werden.", false);
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveSelectedPlaceholder()
|
||||||
|
{
|
||||||
|
if (SelectedPlaceholder is not { } selected) return;
|
||||||
|
Placeholders.Remove(selected); SelectedPlaceholder = null; CanExport = false;
|
||||||
|
SetStatus($"Platzhalter „{selected.Name}“ entfernt.", false);
|
||||||
|
}
|
||||||
|
|
||||||
public void Load(LoadedTemplate template, string layoutSource)
|
public void Load(LoadedTemplate template, string layoutSource)
|
||||||
{
|
{
|
||||||
TemplateId = template.Manifest.Id; TemplateName = template.Manifest.Name; Description = template.Manifest.Description;
|
TemplateId = template.Manifest.Id; TemplateName = template.Manifest.Name; Description = template.Manifest.Description;
|
||||||
@@ -118,6 +138,7 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
Placeholders.Clear();
|
Placeholders.Clear();
|
||||||
foreach (var placeholder in template.Manifest.Placeholders)
|
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, DesignerPlaceholder.SampleFor(placeholder.Type)));
|
||||||
|
SelectedPlaceholder = Placeholders.FirstOrDefault();
|
||||||
Assets.Clear(); AssetItems.Clear();
|
Assets.Clear(); AssetItems.Clear();
|
||||||
foreach (var asset in template.Assets) AddOrReplaceAsset(asset.Key, asset.Value, keepName: true);
|
foreach (var asset in template.Assets) AddOrReplaceAsset(asset.Key, asset.Value, keepName: true);
|
||||||
RefreshAssetUsage();
|
RefreshAssetUsage();
|
||||||
@@ -163,6 +184,20 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<PlaceholderDefinition> BuildPlaceholders()
|
||||||
|
{
|
||||||
|
var names = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
var result = new List<PlaceholderDefinition>(Placeholders.Count);
|
||||||
|
foreach (var placeholder in Placeholders)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static ITemplateDataProvider BuildSampleDataProvider(TemplateManifest manifest) =>
|
private static ITemplateDataProvider BuildSampleDataProvider(TemplateManifest manifest) =>
|
||||||
new DesignerDataProvider(manifest.Placeholders.ToDictionary(x => x.Name,
|
new DesignerDataProvider(manifest.Placeholders.ToDictionary(x => x.Name,
|
||||||
x => new DesignerPlaceholder(x.Name, x.Type, x.Required, DesignerPlaceholder.SampleFor(x.Type)).ToValue(),
|
x => new DesignerPlaceholder(x.Name, x.Type, x.Required, DesignerPlaceholder.SampleFor(x.Type)).ToValue(),
|
||||||
|
|||||||
@@ -92,23 +92,34 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Platzhalter & Beispieldaten" Classes="section"/>
|
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Platzhalter & Beispieldaten" Classes="section"/>
|
||||||
<Button Grid.Column="1" Content="+" Click="OnAddPlaceholder"/></Grid>
|
<Button Grid.Column="1" Content="+ Platzhalter" Click="OnAddPlaceholder"/></Grid>
|
||||||
<DataGrid x:Name="PlaceholderGrid" ItemsSource="{Binding Placeholders}" SelectedItem="{Binding SelectedPlaceholder}"
|
<DataGrid x:Name="PlaceholderGrid" ItemsSource="{Binding Placeholders}" SelectedItem="{Binding SelectedPlaceholder}"
|
||||||
AutoGenerateColumns="False" Height="210" CanUserResizeColumns="True">
|
AutoGenerateColumns="False" Height="180" CanUserResizeColumns="True" IsReadOnly="True">
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
|
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
|
||||||
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="105" IsReadOnly="True"/>
|
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="105"/>
|
||||||
<DataGridCheckBoxColumn Header="Pflicht" Binding="{Binding Required}" Width="58"/>
|
<DataGridCheckBoxColumn Header="Pflicht" Binding="{Binding Required}" Width="58"/>
|
||||||
<DataGridTextColumn Header="Beispiel" Binding="{Binding Sample}" Width="*"/>
|
<DataGridTextColumn Header="Beispiel" Binding="{Binding Sample}" Width="*"/>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
<Grid ColumnDefinitions="Auto,10,*">
|
<Border Padding="10" BorderBrush="#CBD5E1" BorderThickness="1" CornerRadius="4">
|
||||||
<Button Content="Markierten Platzhalter entfernen" Click="OnRemovePlaceholder"/>
|
<StackPanel Spacing="8">
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
<TextBlock Text="Ausgewählten Platzhalter bearbeiten" FontWeight="SemiBold"/>
|
||||||
<TextBlock Text="Typ:" VerticalAlignment="Center"/>
|
<Grid ColumnDefinitions="*,10,*">
|
||||||
<ComboBox Width="130" ItemsSource="{Binding PlaceholderTypes}" SelectedItem="{Binding SelectedPlaceholder.Type}"/>
|
<StackPanel><TextBlock Text="Name" Classes="label"/>
|
||||||
|
<TextBox Text="{Binding SelectedPlaceholder.Name, Mode=TwoWay}" PlaceholderText="z. B. Empfaenger"/></StackPanel>
|
||||||
|
<StackPanel Grid.Column="2"><TextBlock Text="Datentyp" Classes="label"/>
|
||||||
|
<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"
|
||||||
|
MinHeight="58" TextWrapping="Wrap" PlaceholderText="Beispieldaten eingeben"/></StackPanel>
|
||||||
|
<Button Content="Ausgewählten Platzhalter entfernen" HorizontalAlignment="Left"
|
||||||
|
Click="OnRemovePlaceholder"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Border>
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Paket-Assets" Classes="section"/>
|
<Grid ColumnDefinitions="*,Auto"><TextBlock Text="Paket-Assets" Classes="section"/>
|
||||||
<Button Grid.Column="1" Content="+ Bild importieren …" Click="OnImportAsset"/></Grid>
|
<Button Grid.Column="1" Content="+ Bild importieren …" Click="OnImportAsset"/></Grid>
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ public partial class MainWindow : Window
|
|||||||
_viewModel.Reset(); _currentPackagePath = null; UpdateDocumentTitle();
|
_viewModel.Reset(); _currentPackagePath = null; UpdateDocumentTitle();
|
||||||
}
|
}
|
||||||
private void OnExit(object? sender, RoutedEventArgs e) => Close();
|
private void OnExit(object? sender, RoutedEventArgs e) => Close();
|
||||||
private void OnAddPlaceholder(object? sender, RoutedEventArgs e) =>
|
private void OnAddPlaceholder(object? sender, RoutedEventArgs e) => _viewModel.AddPlaceholder();
|
||||||
_viewModel.Placeholders.Add(new($"Feld{_viewModel.Placeholders.Count + 1}", PlaceholderType.Text, false, "Beispiel"));
|
|
||||||
private void OnAddMetadata(object? sender, RoutedEventArgs e)
|
private void OnAddMetadata(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var item = new DesignerMetadata($"custom-{_viewModel.MetadataItems.Count + 1}", "");
|
var item = new DesignerMetadata($"custom-{_viewModel.MetadataItems.Count + 1}", "");
|
||||||
@@ -39,11 +38,7 @@ public partial class MainWindow : Window
|
|||||||
if (_viewModel.SelectedMetadata is not { } selected) return;
|
if (_viewModel.SelectedMetadata is not { } selected) return;
|
||||||
_viewModel.MetadataItems.Remove(selected); _viewModel.SelectedMetadata = null; _viewModel.CanExport = false;
|
_viewModel.MetadataItems.Remove(selected); _viewModel.SelectedMetadata = null; _viewModel.CanExport = false;
|
||||||
}
|
}
|
||||||
private void OnRemovePlaceholder(object? sender, RoutedEventArgs e)
|
private void OnRemovePlaceholder(object? sender, RoutedEventArgs e) => _viewModel.RemoveSelectedPlaceholder();
|
||||||
{
|
|
||||||
if (this.FindControl<DataGrid>("PlaceholderGrid")?.SelectedItem is DesignerPlaceholder selected)
|
|
||||||
_viewModel.Placeholders.Remove(selected);
|
|
||||||
}
|
|
||||||
private void OnAddElement(object? sender, RoutedEventArgs e)
|
private void OnAddElement(object? sender, RoutedEventArgs e)
|
||||||
{ try { _viewModel.AddElement(); } catch (Exception ex) { _viewModel.SetStatus(ex.Message, true); } }
|
{ try { _viewModel.AddElement(); } catch (Exception ex) { _viewModel.SetStatus(ex.Message, true); } }
|
||||||
private void OnMeasureOverlayMode(object? sender, RoutedEventArgs e)
|
private void OnMeasureOverlayMode(object? sender, RoutedEventArgs e)
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user