This commit is contained in:
@@ -80,6 +80,40 @@ public sealed class ProjectLifecycleTests
|
|||||||
Assert.True(viewModel.HasNoSelectedPlaceholder);
|
Assert.True(viewModel.HasNoSelectedPlaceholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Anwesenheitskalender", "Student.AttendanceCalendar", "DRAWBOX", "90")]
|
||||||
|
[InlineData("Fehltage nach Datum", "Student.AbsenceDays", "FLOWDRAWBOX", "140")]
|
||||||
|
public void Sonderinhalt_AusKatalog_BereitetPlatzhalterUndElementVor(
|
||||||
|
string displayName, string placeholderName, string elementType, string height)
|
||||||
|
{
|
||||||
|
var viewModel = new DesignerViewModel();
|
||||||
|
viewModel.Placeholders.Clear();
|
||||||
|
viewModel.SelectedSpecialContent = viewModel.SpecialContents.Single(x => x.DisplayName == displayName);
|
||||||
|
|
||||||
|
viewModel.PrepareSelectedSpecialContent();
|
||||||
|
|
||||||
|
var placeholder = Assert.Single(viewModel.Placeholders);
|
||||||
|
Assert.Equal(placeholderName, placeholder.Name);
|
||||||
|
Assert.Equal(LehrerApp.Templating.PlaceholderType.Drawing, placeholder.Type);
|
||||||
|
Assert.Same(placeholder, viewModel.SelectedPlaceholder);
|
||||||
|
Assert.Equal(elementType, viewModel.NewElementType);
|
||||||
|
Assert.Equal("$" + placeholderName, viewModel.NewContent);
|
||||||
|
Assert.Equal("170", viewModel.NewWidth);
|
||||||
|
Assert.Equal(height, viewModel.NewHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Sonderinhalt_WirdNichtDoppeltDeklariert()
|
||||||
|
{
|
||||||
|
var viewModel = new DesignerViewModel();
|
||||||
|
viewModel.SelectedSpecialContent = viewModel.SpecialContents.Single(x =>
|
||||||
|
x.PlaceholderName == "Student.AttendanceCalendar");
|
||||||
|
|
||||||
|
viewModel.PrepareSelectedSpecialContent();
|
||||||
|
|
||||||
|
Assert.Single(viewModel.Placeholders, x => x.Name == "Student.AttendanceCalendar");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DoppeltePlatzhalternamen_WerdenVerstaendlichAbgelehnt()
|
public void DoppeltePlatzhalternamen_WerdenVerstaendlichAbgelehnt()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,12 +48,14 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
[ObservableProperty] private string _overlayPageUnit = "mm";
|
[ObservableProperty] private string _overlayPageUnit = "mm";
|
||||||
[ObservableProperty] private string _selectedPageTemplate = "first";
|
[ObservableProperty] private string _selectedPageTemplate = "first";
|
||||||
[ObservableProperty] private DesignerPreviewPage? _selectedPreviewPage;
|
[ObservableProperty] private DesignerPreviewPage? _selectedPreviewPage;
|
||||||
|
[ObservableProperty] private SpecialContentItem? _selectedSpecialContent;
|
||||||
|
|
||||||
public IReadOnlyList<string> Units { get; } = ["mm", "cm", "pt", "in"];
|
public IReadOnlyList<string> Units { get; } = ["mm", "cm", "pt", "in"];
|
||||||
public IReadOnlyList<PlaceholderType> PlaceholderTypes { get; } = Enum.GetValues<PlaceholderType>();
|
public IReadOnlyList<PlaceholderType> PlaceholderTypes { get; } = Enum.GetValues<PlaceholderType>();
|
||||||
public IReadOnlyList<string> ElementTypes { get; } =
|
public IReadOnlyList<string> ElementTypes { get; } =
|
||||||
["TEXT", "TEXTBOX", "FLOWBOX", "DRAWBOX", "FLOWDRAWBOX", "IMG", "TABLE", "CHART"];
|
["TEXT", "TEXTBOX", "FLOWBOX", "DRAWBOX", "FLOWDRAWBOX", "IMG", "TABLE", "CHART"];
|
||||||
public IReadOnlyList<string> ElementScopes { get; } = ["Seitenvorlage (fest)", "Content-Flow"];
|
public IReadOnlyList<string> ElementScopes { get; } = ["Seitenvorlage (fest)", "Content-Flow"];
|
||||||
|
public IReadOnlyList<SpecialContentItem> SpecialContents { get; } = SpecialContentCatalog.Items;
|
||||||
public ObservableCollection<DesignerPlaceholder> Placeholders { get; } =
|
public ObservableCollection<DesignerPlaceholder> Placeholders { get; } =
|
||||||
[
|
[
|
||||||
new("Datum", PlaceholderType.Date, true, DateTime.Today.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)),
|
new("Datum", PlaceholderType.Date, true, DateTime.Today.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)),
|
||||||
@@ -78,7 +80,11 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
public bool HasSelectedPlaceholder => SelectedPlaceholder is not null;
|
public bool HasSelectedPlaceholder => SelectedPlaceholder is not null;
|
||||||
public bool HasNoSelectedPlaceholder => SelectedPlaceholder is null;
|
public bool HasNoSelectedPlaceholder => SelectedPlaceholder is null;
|
||||||
|
|
||||||
public DesignerViewModel() => SelectedPlaceholder = Placeholders.FirstOrDefault();
|
public DesignerViewModel()
|
||||||
|
{
|
||||||
|
SelectedPlaceholder = Placeholders.FirstOrDefault();
|
||||||
|
SelectedSpecialContent = SpecialContents.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnSelectedPlaceholderChanged(DesignerPlaceholder? value)
|
partial void OnSelectedPlaceholderChanged(DesignerPlaceholder? value)
|
||||||
{
|
{
|
||||||
@@ -198,6 +204,32 @@ public partial class DesignerViewModel : ObservableObject
|
|||||||
return placeholder;
|
return placeholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Übernimmt einen lesbar benannten LehrerApp-Sonderinhalt in das normale
|
||||||
|
/// Elementformular und legt seine Drawing-Deklaration an, falls sie noch fehlt.</summary>
|
||||||
|
public void PrepareSelectedSpecialContent()
|
||||||
|
{
|
||||||
|
if (SelectedSpecialContent is not { } special)
|
||||||
|
throw new InvalidOperationException("Bitte zuerst einen Sonderinhalt auswählen.");
|
||||||
|
var placeholder = Placeholders.FirstOrDefault(p => p.Name == special.PlaceholderName);
|
||||||
|
if (placeholder is not null && placeholder.Type != PlaceholderType.Drawing)
|
||||||
|
throw new InvalidDataException(
|
||||||
|
$"Der vorhandene Platzhalter „{special.PlaceholderName}“ hat nicht den Typ Drawing.");
|
||||||
|
if (placeholder is null)
|
||||||
|
{
|
||||||
|
placeholder = new DesignerPlaceholder(special.PlaceholderName, PlaceholderType.Drawing,
|
||||||
|
false, special.Description);
|
||||||
|
Placeholders.Add(placeholder);
|
||||||
|
}
|
||||||
|
SelectedPlaceholder = placeholder;
|
||||||
|
NewElementType = special.RecommendedElementType;
|
||||||
|
NewContent = "$" + special.PlaceholderName;
|
||||||
|
NewWidth = special.RecommendedWidth;
|
||||||
|
NewHeight = special.RecommendedHeight;
|
||||||
|
NewAttributes = "";
|
||||||
|
CanExport = false;
|
||||||
|
SetStatus($"„{special.DisplayName}“ vorbereitet. Position und Größe prüfen, dann ins Layout übernehmen.", false);
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveSelectedPlaceholder()
|
public void RemoveSelectedPlaceholder()
|
||||||
{
|
{
|
||||||
if (SelectedPlaceholder is not { } selected) return;
|
if (SelectedPlaceholder is not { } selected) return;
|
||||||
@@ -703,6 +735,48 @@ public sealed record DesignerPreviewPage(int Number, Bitmap Image)
|
|||||||
public string Display => $"Dokumentseite {Number}";
|
public string Display => $"Dokumentseite {Number}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed record SpecialContentItem(string DisplayName, string PlaceholderName, string Description,
|
||||||
|
string RecommendedElementType, string RecommendedWidth, string RecommendedHeight);
|
||||||
|
|
||||||
|
public static class SpecialContentCatalog
|
||||||
|
{
|
||||||
|
public static IReadOnlyList<SpecialContentItem> Items { get; } =
|
||||||
|
[
|
||||||
|
new("Anwesenheitskalender", "Student.AttendanceCalendar",
|
||||||
|
"Monatskalender mit farbigen Anwesenheitsmarkern; Zeitraum und Größe werden beim Erstellen des Briefs gewählt.",
|
||||||
|
"DRAWBOX", "170", "90"),
|
||||||
|
new("Fehltage nach Datum", "Student.AbsenceDays",
|
||||||
|
"Chronologische Liste mit Datum, Fehlzeit oder ganzem Fehltag sowie Entschuldigungsstatus.",
|
||||||
|
"FLOWDRAWBOX", "170", "140"),
|
||||||
|
];
|
||||||
|
|
||||||
|
public static DrawingValue SampleDrawing(string placeholderName) => placeholderName switch
|
||||||
|
{
|
||||||
|
"Student.AttendanceCalendar" => new DrawingValue(
|
||||||
|
[
|
||||||
|
new DrawString(2, 2, "Anwesenheit · Erika Beispiel", 10, "#1F2937", Bold: true),
|
||||||
|
new DrawRectangle(2, 14, 22, 12, "#D1D5DB", .4f, "#FFFFFF"),
|
||||||
|
new DrawString(9, 16, "12", 7, "#374151"),
|
||||||
|
new DrawRectangle(26, 14, 22, 12, "#C62828", .4f, "#C62828"),
|
||||||
|
new DrawString(34, 16, "U", 7, "#FFFFFF", Bold: true),
|
||||||
|
new DrawRectangle(50, 14, 22, 12, "#2E7D32", .4f, "#2E7D32"),
|
||||||
|
new DrawString(58, 16, "E", 7, "#FFFFFF", Bold: true),
|
||||||
|
], 28),
|
||||||
|
"Student.AbsenceDays" => new DrawingValue(
|
||||||
|
[
|
||||||
|
new DrawString(2, 2, "Fehltage · Erika Beispiel", 10, "#1F2937", Bold: true),
|
||||||
|
new DrawRectangle(2, 14, 166, 11, "#CBD5E1", .4f, "#F3F4F6"),
|
||||||
|
new DrawString(4, 15, "Datum", 7, "#374151", Bold: true),
|
||||||
|
new DrawString(42, 15, "Umfang", 7, "#374151", Bold: true),
|
||||||
|
new DrawString(116, 15, "Status", 7, "#374151", Bold: true),
|
||||||
|
new DrawString(4, 27, "03.09.2026", 7, "#374151"),
|
||||||
|
new DrawString(42, 27, "Ganzer Fehltag", 7, "#374151"),
|
||||||
|
new DrawString(116, 27, "Entschuldigt", 7, "#2E7D32"),
|
||||||
|
], 38),
|
||||||
|
_ => DesignerPlaceholder.GenericSampleDrawing(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public partial class DesignerAsset(string name, int pixelWidth, int pixelHeight, long byteCount) : ObservableObject
|
public partial class DesignerAsset(string name, int pixelWidth, int pixelHeight, long byteCount) : ObservableObject
|
||||||
{
|
{
|
||||||
[ObservableProperty] private int _usageCount;
|
[ObservableProperty] private int _usageCount;
|
||||||
@@ -757,7 +831,7 @@ public partial class DesignerPlaceholder : ObservableObject
|
|||||||
PlaceholderType.Image => new ImageValue([], "image/png"),
|
PlaceholderType.Image => new ImageValue([], "image/png"),
|
||||||
PlaceholderType.Table => ParseTable(Sample),
|
PlaceholderType.Table => ParseTable(Sample),
|
||||||
PlaceholderType.Chart => ParseChart(Sample),
|
PlaceholderType.Chart => ParseChart(Sample),
|
||||||
PlaceholderType.Drawing => SampleDrawing(),
|
PlaceholderType.Drawing => SpecialContentCatalog.SampleDrawing(Name),
|
||||||
_ => new TextValue(Sample),
|
_ => new TextValue(Sample),
|
||||||
};
|
};
|
||||||
public static string SampleFor(PlaceholderType type) => type switch
|
public static string SampleFor(PlaceholderType type) => type switch
|
||||||
@@ -772,7 +846,7 @@ public partial class DesignerPlaceholder : ObservableObject
|
|||||||
}
|
}
|
||||||
private static ChartValue ParseChart(string value) => new([new("Werte", value.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
private static ChartValue ParseChart(string value) => new([new("Werte", value.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select((x, i) => { var parts = x.Split(':', 2); return new ChartPoint(parts[0], parts.Length == 2 && decimal.TryParse(parts[1], CultureInfo.InvariantCulture, out var y) ? y : i + 1); }).ToList())]);
|
.Select((x, i) => { var parts = x.Split(':', 2); return new ChartPoint(parts[0], parts.Length == 2 && decimal.TryParse(parts[1], CultureInfo.InvariantCulture, out var y) ? y : i + 1); }).ToList())]);
|
||||||
private static DrawingValue SampleDrawing() => new DrawingValue(
|
internal static DrawingValue GenericSampleDrawing() => new DrawingValue(
|
||||||
[new DrawRectangle(0, 0, 80, 24, "#2563EB", 0.8f, "#EFF6FF"),
|
[new DrawRectangle(0, 0, 80, 24, "#2563EB", 0.8f, "#EFF6FF"),
|
||||||
new DrawString(4, 4, "Dynamischer Inhalt", 10, "#1E3A8A", Bold: true),
|
new DrawString(4, 4, "Dynamischer Inhalt", 10, "#1E3A8A", Bold: true),
|
||||||
new MoveTo(4, 19), new LineTo(76, 19, "#93C5FD", 0.6f)], 24);
|
new MoveTo(4, 19), new LineTo(76, 19, "#93C5FD", 0.6f)], 24);
|
||||||
|
|||||||
@@ -213,6 +213,21 @@
|
|||||||
<ScrollViewer Padding="8">
|
<ScrollViewer Padding="8">
|
||||||
<StackPanel Spacing="12">
|
<StackPanel Spacing="12">
|
||||||
<TextBlock Text="Element hinzufügen" Classes="section"/>
|
<TextBlock Text="Element hinzufügen" Classes="section"/>
|
||||||
|
<Border BorderBrush="#93C5FD" BorderThickness="1" Background="#EFF6FF"
|
||||||
|
CornerRadius="6" Padding="12">
|
||||||
|
<StackPanel Spacing="7">
|
||||||
|
<TextBlock Text="LehrerApp-Sonderinhalte" FontWeight="SemiBold" Foreground="#1E3A8A"/>
|
||||||
|
<TextBlock Text="Kein technischer Platzhaltername nötig: Inhalt auswählen und übernehmen."
|
||||||
|
FontSize="11" Foreground="#475569" TextWrapping="Wrap"/>
|
||||||
|
<ComboBox ItemsSource="{Binding SpecialContents}"
|
||||||
|
SelectedItem="{Binding SelectedSpecialContent, Mode=TwoWay}"
|
||||||
|
DisplayMemberBinding="{Binding DisplayName}"/>
|
||||||
|
<TextBlock Text="{Binding SelectedSpecialContent.Description}" FontSize="11"
|
||||||
|
Foreground="#475569" TextWrapping="Wrap"/>
|
||||||
|
<Button Content="Für Layout vorbereiten" HorizontalAlignment="Left"
|
||||||
|
Click="OnPrepareSpecialContent"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
<StackPanel><TextBlock Text="Einfügen in" Classes="label"/>
|
<StackPanel><TextBlock Text="Einfügen in" Classes="label"/>
|
||||||
<ComboBox ItemsSource="{Binding ElementScopes}" SelectedItem="{Binding NewElementScope}"/></StackPanel>
|
<ComboBox ItemsSource="{Binding ElementScopes}" SelectedItem="{Binding NewElementScope}"/></StackPanel>
|
||||||
<Grid ColumnDefinitions="*,8,*">
|
<Grid ColumnDefinitions="*,8,*">
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ public partial class MainWindow : Window
|
|||||||
}
|
}
|
||||||
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 OnPrepareSpecialContent(object? sender, RoutedEventArgs e)
|
||||||
|
{ try { _viewModel.PrepareSelectedSpecialContent(); } catch (Exception ex) { _viewModel.SetStatus(ex.Message, true); } }
|
||||||
private void OnAddPageTemplate(object? sender, RoutedEventArgs e)
|
private void OnAddPageTemplate(object? sender, RoutedEventArgs e)
|
||||||
{ try { _viewModel.AddPageTemplate(); } catch (Exception ex) { _viewModel.SetStatus(ex.Message, true); } }
|
{ try { _viewModel.AddPageTemplate(); } catch (Exception ex) { _viewModel.SetStatus(ex.Message, true); } }
|
||||||
private void OnAddContentFlow(object? sender, RoutedEventArgs e)
|
private void OnAddContentFlow(object? sender, RoutedEventArgs e)
|
||||||
|
|||||||
Reference in New Issue
Block a user