Upload via GUI (93 Dateien)
This commit is contained in:
@@ -27,7 +27,7 @@ public partial class MainWindow : Window
|
||||
InitializeComponent();
|
||||
_mode = mode;
|
||||
VersionText.Text = $"Version {Installer.Version}";
|
||||
LicenseText.Text = Installer.LicenseText;
|
||||
ZeigeLizenz(Installer.LicenseText);
|
||||
|
||||
var installed = Installer.InstalledDir;
|
||||
DirBox.Text = installed ?? Installer.DefaultDir;
|
||||
@@ -68,6 +68,107 @@ public partial class MainWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Lizenztext ----------
|
||||
|
||||
/// <summary>
|
||||
/// LICENSE ist für die Textansicht auf ~80 Zeichen umbrochen und eingerückt. Hier wird das
|
||||
/// wieder zu fließendem Text zusammengesetzt, damit der Umbruch zur Fensterbreite passt:
|
||||
/// Leerzeilen trennen Abschnitte, "1. …" sind Überschriften, "- …" Aufzählungspunkte
|
||||
/// (Folgezeilen gehören zum Punkt), alles andere ist Absatztext.
|
||||
/// </summary>
|
||||
private void ZeigeLizenz(string text)
|
||||
{
|
||||
LicensePanel.Children.Clear();
|
||||
var abschnitte = new List<List<string>>();
|
||||
var aktuell = new List<string>();
|
||||
foreach (var zeile in text.Replace("\r", "").Split('\n'))
|
||||
{
|
||||
if (zeile.Trim().Length == 0)
|
||||
{
|
||||
if (aktuell.Count > 0) { abschnitte.Add(aktuell); aktuell = []; }
|
||||
}
|
||||
else aktuell.Add(zeile);
|
||||
}
|
||||
if (aktuell.Count > 0) abschnitte.Add(aktuell);
|
||||
|
||||
for (int a = 0; a < abschnitte.Count; a++)
|
||||
{
|
||||
var abschnitt = abschnitte[a];
|
||||
int i = 0;
|
||||
if (a == 0)
|
||||
{
|
||||
LicensePanel.Children.Add(LizenzText(abschnitt[0].Trim(), 14, FontWeights.SemiBold, "TextBrush", 0));
|
||||
i = 1;
|
||||
}
|
||||
else if (System.Text.RegularExpressions.Regex.IsMatch(abschnitt[0], @"^\s*\d+\.\s"))
|
||||
{
|
||||
LicensePanel.Children.Add(LizenzText(abschnitt[0].Trim(), 13, FontWeights.SemiBold, "TextBrush", 14));
|
||||
i = 1;
|
||||
}
|
||||
|
||||
// nach einer Überschrift eng anschließen, sonst Abstand zum vorigen Abschnitt
|
||||
double absatzAbstand = i == 1 && a > 0 ? 4 : 10;
|
||||
var absatz = new System.Text.StringBuilder();
|
||||
string? punkt = null;
|
||||
void AbsatzFertig()
|
||||
{
|
||||
if (absatz.Length == 0) return;
|
||||
LicensePanel.Children.Add(LizenzText(absatz.ToString(), 12.5, FontWeights.Normal, "TextSecondaryBrush", absatzAbstand));
|
||||
absatz.Clear();
|
||||
}
|
||||
void PunktFertig()
|
||||
{
|
||||
if (punkt is null) return;
|
||||
LicensePanel.Children.Add(LizenzPunkt(punkt));
|
||||
punkt = null;
|
||||
}
|
||||
|
||||
for (; i < abschnitt.Count; i++)
|
||||
{
|
||||
var t = abschnitt[i].Trim();
|
||||
if (t.StartsWith("- "))
|
||||
{
|
||||
AbsatzFertig();
|
||||
PunktFertig();
|
||||
punkt = t.Substring(2).Trim();
|
||||
}
|
||||
else if (punkt is not null) punkt += " " + t;
|
||||
else absatz.Append(absatz.Length > 0 ? " " : "").Append(t);
|
||||
}
|
||||
AbsatzFertig();
|
||||
PunktFertig();
|
||||
}
|
||||
}
|
||||
|
||||
private TextBlock LizenzText(string text, double groesse, FontWeight gewicht, string farbe, double abstandOben)
|
||||
{
|
||||
var tb = new TextBlock
|
||||
{
|
||||
Text = text,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
FontSize = groesse,
|
||||
FontWeight = gewicht,
|
||||
LineHeight = groesse + 6,
|
||||
Margin = new Thickness(0, abstandOben, 0, 0),
|
||||
};
|
||||
tb.SetResourceReference(TextBlock.ForegroundProperty, farbe);
|
||||
return tb;
|
||||
}
|
||||
|
||||
// Aufzählungspunkt mit hängendem Einzug (umbrochene Zeilen beginnen unter dem Text, nicht unter dem Punkt)
|
||||
private Grid LizenzPunkt(string text)
|
||||
{
|
||||
var grid = new Grid { Margin = new Thickness(4, 4, 0, 0) };
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(16) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
var punkt = LizenzText("•", 12.5, FontWeights.Normal, "AccentBrush", 0);
|
||||
var inhalt = LizenzText(text, 12.5, FontWeights.Normal, "TextSecondaryBrush", 0);
|
||||
Grid.SetColumn(inhalt, 1);
|
||||
grid.Children.Add(punkt);
|
||||
grid.Children.Add(inhalt);
|
||||
return grid;
|
||||
}
|
||||
|
||||
// ---------- Seiten ----------
|
||||
|
||||
private UIElement? _shownPage;
|
||||
@@ -182,13 +283,14 @@ public partial class MainWindow : Window
|
||||
var dir = DirBox.Text.Trim();
|
||||
if (!IsFullPath(dir) || dir.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
|
||||
{
|
||||
MessageBox.Show(this, "Bitte einen gültigen Ordner angeben, z. B. C:\\Spiele\\AeroMC.", "AeroMC Setup");
|
||||
await Frage("Ungültiger Ordner", "Bitte einen vollständigen Ordner angeben, z. B. C:\\Spiele\\AeroMC.", "OK");
|
||||
return;
|
||||
}
|
||||
if (dir.TrimEnd('\\', '/').Length > Installer.MaxDirLength)
|
||||
{
|
||||
MessageBox.Show(this, $"Der Ordnerpfad ist zu lang ({dir.TrimEnd('\\', '/').Length} Zeichen). Windows erlaubt hier höchstens " +
|
||||
$"{Installer.MaxDirLength} Zeichen – bitte einen kürzeren Ordner wählen, z. B. C:\\Spiele\\AeroMC.", "AeroMC Setup");
|
||||
await Frage("Ordnerpfad zu lang",
|
||||
$"Der Pfad hat {dir.TrimEnd('\\', '/').Length} Zeichen, Windows erlaubt hier höchstens {Installer.MaxDirLength}. " +
|
||||
"Bitte einen kürzeren Ordner wählen, z. B. C:\\Spiele\\AeroMC.", "OK");
|
||||
return;
|
||||
}
|
||||
if (HasImport) ShowPage(PageNumImport);
|
||||
@@ -423,18 +525,87 @@ public partial class MainWindow : Window
|
||||
|
||||
private void Minimize_Click(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
|
||||
|
||||
private void Cancel_Click(object sender, RoutedEventArgs e)
|
||||
private async void Cancel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_busy) return;
|
||||
if (!_done && _mode == SetupMode.Install && _page > 0 &&
|
||||
MessageBox.Show(this, "Installation wirklich abbrechen?", "AeroMC Setup", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||
return;
|
||||
if (_busy || _dialog is not null) return;
|
||||
if (!_done && _mode == SetupMode.Install && _page > 0)
|
||||
{
|
||||
bool abbrechen = await Frage("Installation abbrechen?",
|
||||
"AeroMC wird nicht installiert. Du kannst das Setup später jederzeit erneut starten.",
|
||||
"Ja, abbrechen", "Weiter installieren", "\uE897");
|
||||
if (!abbrechen) return;
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
// ---------- eigene Rückfragen im Setup-Look ----------
|
||||
|
||||
private TaskCompletionSource<bool>? _dialog;
|
||||
|
||||
/// <summary>
|
||||
/// Zeigt eine Rückfrage über dem Fenster. Mit <paramref name="zurueck"/>: zwei Knöpfe –
|
||||
/// der hervorgehobene ist der sichere Weg zurück (liefert false), der andere bestätigt (true).
|
||||
/// Ohne: nur ein Hinweis mit einem Knopf (liefert true).
|
||||
/// </summary>
|
||||
private Task<bool> Frage(string titel, string text, string bestaetigen, string? zurueck = null, string symbol = "\uE7BA")
|
||||
{
|
||||
_dialog?.TrySetResult(false);
|
||||
_dialog = new TaskCompletionSource<bool>();
|
||||
DialogTitle.Text = titel;
|
||||
DialogText.Text = text;
|
||||
DialogIcon.Text = symbol;
|
||||
if (zurueck is null)
|
||||
{
|
||||
DialogSecondary.Visibility = Visibility.Collapsed;
|
||||
DialogPrimary.Content = bestaetigen;
|
||||
}
|
||||
else
|
||||
{
|
||||
DialogSecondary.Visibility = Visibility.Visible;
|
||||
DialogSecondary.Content = bestaetigen;
|
||||
DialogPrimary.Content = zurueck;
|
||||
}
|
||||
|
||||
DialogOverlay.Visibility = Visibility.Visible;
|
||||
var ein = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160));
|
||||
DialogOverlay.BeginAnimation(OpacityProperty, ein);
|
||||
var groesse = new DoubleAnimation(0.94, 1, TimeSpan.FromMilliseconds(220)) { EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut } };
|
||||
DialogScale.BeginAnimation(ScaleTransform.ScaleXProperty, groesse);
|
||||
DialogScale.BeginAnimation(ScaleTransform.ScaleYProperty, groesse);
|
||||
DialogPrimary.Focus();
|
||||
return _dialog.Task;
|
||||
}
|
||||
|
||||
private void SchliesseFrage(bool ergebnis)
|
||||
{
|
||||
if (_dialog is null) return;
|
||||
var tcs = _dialog;
|
||||
_dialog = null;
|
||||
DialogOverlay.Visibility = Visibility.Collapsed;
|
||||
tcs.TrySetResult(ergebnis);
|
||||
}
|
||||
|
||||
// hervorgehobener Knopf: bei Rückfragen "zurück" (false), beim reinen Hinweis "OK" (true)
|
||||
private void DialogPrimary_Click(object sender, RoutedEventArgs e) =>
|
||||
SchliesseFrage(DialogSecondary.Visibility != Visibility.Visible);
|
||||
|
||||
private void DialogSecondary_Click(object sender, RoutedEventArgs e) => SchliesseFrage(true);
|
||||
|
||||
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
|
||||
{
|
||||
// solange die Rückfrage offen ist: Enter = hervorgehobener Knopf, Esc = zurück
|
||||
if (_dialog is not null)
|
||||
{
|
||||
if (e.Key == System.Windows.Input.Key.Escape) { SchliesseFrage(DialogSecondary.Visibility != Visibility.Visible); e.Handled = true; return; }
|
||||
if (e.Key == System.Windows.Input.Key.Enter) { DialogPrimary_Click(this, new RoutedEventArgs()); e.Handled = true; return; }
|
||||
}
|
||||
base.OnPreviewKeyDown(e);
|
||||
}
|
||||
|
||||
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
if (_busy) e.Cancel = true; // während der Installation nicht schließen
|
||||
_dialog?.TrySetResult(false);
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user