515 lines
25 KiB
C#
515 lines
25 KiB
C#
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text.Json;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Interop;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Animation;
|
|
using System.Windows.Media.Imaging;
|
|
using System.Windows.Shapes;
|
|
using MmdPdf.Services;
|
|
|
|
namespace MmdPdf
|
|
{
|
|
// ============================================================
|
|
// Themed dialog - replaces MessageBox for dark-UI consistency
|
|
// ============================================================
|
|
internal static class KillerDialog
|
|
{
|
|
// Pulls the current theme brush at call time so dialogs respect light/dark/HC themes.
|
|
private static SolidColorBrush R(string key)
|
|
=> (SolidColorBrush)Application.Current.Resources[key];
|
|
|
|
private static string L(string key, string fallback)
|
|
=> Application.Current.TryFindResource(key) as string ?? fallback;
|
|
|
|
// Carries the checkbox state of the last Show() call back to ShowWithCheckbox. Dialogs are
|
|
// modal and UI-thread only, so a shared field is safe and avoids a duplicate dialog body.
|
|
private static bool _lastCheckboxChecked;
|
|
|
|
#pragma warning disable IDE0060 // image intentionally kept for API parity with MessageBox; not yet rendered
|
|
public static MessageBoxResult Show(
|
|
Window? owner,
|
|
string message,
|
|
string title = "MmdPdf",
|
|
MessageBoxButton buttons = MessageBoxButton.OK,
|
|
MessageBoxImage image = MessageBoxImage.None,
|
|
bool fadeClose = true,
|
|
string? checkboxText = null,
|
|
MessageBoxResult? defaultResult = null)
|
|
#pragma warning restore IDE0060
|
|
{
|
|
var result = MessageBoxResult.OK;
|
|
bool boxChecked = false;
|
|
|
|
var win = new Window
|
|
{
|
|
Title = title,
|
|
Width = 380,
|
|
SizeToContent = SizeToContent.Height
|
|
};
|
|
DialogChrome.Configure(win, owner, fade: fadeClose);
|
|
|
|
var outerBorder = new Border
|
|
{
|
|
Background = R("MenuBackgroundBrush"),
|
|
BorderBrush = UiKit.Brush("DialogFrameBrush"),
|
|
BorderThickness = Application.Current.TryFindResource("DialogFrameThickness") is Thickness dft ? dft : new Thickness(1),
|
|
Padding = Application.Current.TryFindResource("DialogFramePadding") is Thickness dfp ? dfp : new Thickness(0),
|
|
CornerRadius = UiKit.RadWindow,
|
|
Margin = Application.Current.TryFindResource("DialogHaloMargin") is Thickness hm ? hm : new Thickness(10),
|
|
Effect = UiKit.ShadowDialog()
|
|
};
|
|
|
|
var root = new StackPanel();
|
|
|
|
// Title bar
|
|
var titleBar = new Border
|
|
{
|
|
// Transparent so the dialog-wide film grain shows through the title bar too (it sits
|
|
// over the same BgModal surface, so it still reads as one continuous surface).
|
|
Background = Application.Current.TryFindResource("UseDialogCaption") is true ? UiKit.Brush("TitleBarBrush") : Brushes.Transparent,
|
|
Padding = new Thickness(16, 10, 16, 10),
|
|
CornerRadius = new CornerRadius(5, 5, 0, 0)
|
|
};
|
|
titleBar.MouseLeftButtonDown += (_, e) => { if (e.ButtonState == MouseButtonState.Pressed) win.DragMove(); };
|
|
// When the title is just "MmdPdf", render it as the main window's wordmark - "Killer"
|
|
// in the primary text color and "PDF" in the green logo accent, bold, with a soft shadow.
|
|
if (title == "MmdPdf")
|
|
{
|
|
var wm = new StackPanel { Orientation = Orientation.Horizontal };
|
|
var wmTb = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
|
|
wmTb.Inlines.Add(new System.Windows.Documents.Run("MMD ") { FontFamily = UiKit.WordmarkFont, FontWeight = FontWeights.Normal, FontSize = 15, Foreground = R("TextBrush") });
|
|
wmTb.Inlines.Add(new System.Windows.Documents.Run("PDF") { FontFamily = UiKit.WordmarkFontPdf, FontWeight = FontWeights.Bold, FontSize = 19.5, Foreground = R("AccentLogo") });
|
|
wm.Children.Add(wmTb);
|
|
// No DropShadowEffect on the text - it rasterizes and blurs the wordmark. Kept crisp.
|
|
titleBar.Child = wm;
|
|
}
|
|
else
|
|
{
|
|
titleBar.Child = new TextBlock
|
|
{
|
|
Text = title,
|
|
Foreground = R("PrimaryBrush"),
|
|
FontWeight = FontWeights.Bold, // blue title -> bold
|
|
FontSize = 14,
|
|
FontFamily = UiKit.MonoFont
|
|
};
|
|
}
|
|
if (Application.Current.TryFindResource("UseDialogCaption") is true)
|
|
titleBar = DialogChrome.BuildTitleBar(win, owner, title, () => { result = MessageBoxResult.Cancel; win.Close(); });
|
|
titleBar.Height = Application.Current.TryFindResource("DialogTitleBarHeight") is double titleHeight ? titleHeight : double.NaN;
|
|
root.Children.Add(titleBar);
|
|
|
|
// Message
|
|
var msgBorder = new Border
|
|
{
|
|
Padding = new Thickness(20, 16, 20, 8),
|
|
Child = new TextBlock
|
|
{
|
|
Text = message,
|
|
Foreground = R("TextBrush"),
|
|
FontSize = 13,
|
|
TextWrapping = TextWrapping.Wrap
|
|
}
|
|
};
|
|
root.Children.Add(msgBorder);
|
|
|
|
// Optional checkbox (e.g. "Remember my choice"). Extra top padding sets it apart from the message.
|
|
if (checkboxText is not null)
|
|
{
|
|
var chk = UiKit.CheckBox(checkboxText);
|
|
chk.Margin = new Thickness(20, 10, 20, 4);
|
|
chk.Checked += (_, _2) => boxChecked = true;
|
|
chk.Unchecked += (_, _2) => boxChecked = false;
|
|
root.Children.Add(chk);
|
|
}
|
|
|
|
// Buttons
|
|
var btnPanel = new StackPanel
|
|
{
|
|
Orientation = Orientation.Horizontal,
|
|
HorizontalAlignment = HorizontalAlignment.Right
|
|
};
|
|
|
|
// Build a minimal ControlTemplate so Background binds correctly and
|
|
// WPF's default blue hover chrome can't override our colors.
|
|
Button MakeBtn(string label, MessageBoxResult res, bool accent = false)
|
|
{
|
|
// Enter triggers the primary action. Normally that's the accent button; a caller can
|
|
// override which button is the default (e.g. the quit prompt makes the safe "No" the
|
|
// default), and the accent highlight follows so the Enter target is obvious.
|
|
bool isDefault = defaultResult is MessageBoxResult dr ? res == dr : accent;
|
|
// Shared themed button (UiKit.Make) so this dialog matches the print dialog et al.
|
|
var btn = UiKit.Make(label, isDefault);
|
|
btn.Margin = new Thickness(8, 0, 0, 0);
|
|
btn.IsDefault = isDefault;
|
|
btn.IsCancel = res == MessageBoxResult.Cancel; // Esc triggers Cancel where there is one
|
|
btn.Click += (_, _2) => { result = res; win.Close(); };
|
|
return btn;
|
|
}
|
|
|
|
switch (buttons)
|
|
{
|
|
case MessageBoxButton.OK:
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_OK", "OK"), MessageBoxResult.OK, accent: true));
|
|
break;
|
|
case MessageBoxButton.OKCancel:
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_OK", "OK"), MessageBoxResult.OK, accent: true));
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_Cancel", "Cancel"), MessageBoxResult.Cancel));
|
|
break;
|
|
case MessageBoxButton.YesNo:
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_Yes", "Yes"), MessageBoxResult.Yes, accent: true));
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_No", "No"), MessageBoxResult.No));
|
|
break;
|
|
case MessageBoxButton.YesNoCancel:
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_Yes", "Yes"), MessageBoxResult.Yes, accent: true));
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_No", "No"), MessageBoxResult.No));
|
|
btnPanel.Children.Add(MakeBtn(L("Str_Btn_Cancel", "Cancel"), MessageBoxResult.Cancel));
|
|
break;
|
|
}
|
|
|
|
root.Children.Add(new Border
|
|
{
|
|
Padding = new Thickness(16, 8, 16, 16),
|
|
Child = btnPanel
|
|
});
|
|
|
|
// Paint the same film-grain texture the app's panels use, behind the content, so the
|
|
// dialog reads as part of the same surface family instead of a flat box.
|
|
var contentGrid = new Grid();
|
|
var grain = (owner as MainWindow)?.GrainTexture;
|
|
if (grain is not null)
|
|
{
|
|
double grainOpacity = Application.Current.Resources["GrainOpacity"] is double go ? go : 0.05;
|
|
contentGrid.Children.Add(new Border
|
|
{
|
|
CornerRadius = new CornerRadius(6),
|
|
IsHitTestVisible = false,
|
|
Opacity = grainOpacity,
|
|
Background = new System.Windows.Media.ImageBrush(grain)
|
|
{
|
|
TileMode = System.Windows.Media.TileMode.Tile,
|
|
ViewportUnits = System.Windows.Media.BrushMappingMode.Absolute,
|
|
Viewport = new Rect(0, 0, 256, 256),
|
|
Stretch = System.Windows.Media.Stretch.None
|
|
}
|
|
});
|
|
}
|
|
contentGrid.Children.Add(root);
|
|
win.Content = DialogChrome.WrapContent(owner, contentGrid);
|
|
win.ShowDialog();
|
|
_lastCheckboxChecked = boxChecked;
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Like <see cref="Show"/> but with a custom set of buttons. Returns the index of the clicked
|
|
/// button, or -1 if the dialog was closed without a choice. The button at <paramref name="accentIndex"/>
|
|
/// is rendered as the primary (accent) action.
|
|
/// </summary>
|
|
public static int ShowChoices(
|
|
Window? owner,
|
|
string message,
|
|
string[] labels,
|
|
int accentIndex = 0,
|
|
string title = "MmdPdf")
|
|
{
|
|
int result = -1;
|
|
|
|
var win = new Window { Title = title, MinWidth = 380, MaxWidth = 760, SizeToContent = SizeToContent.WidthAndHeight };
|
|
DialogChrome.Configure(win, owner, fade: true);
|
|
|
|
var outerBorder = new Border
|
|
{
|
|
Background = R("MenuBackgroundBrush"),
|
|
BorderBrush = UiKit.Brush("DialogFrameBrush"),
|
|
BorderThickness = Application.Current.TryFindResource("DialogFrameThickness") is Thickness dft ? dft : new Thickness(1),
|
|
Padding = Application.Current.TryFindResource("DialogFramePadding") is Thickness dfp ? dfp : new Thickness(0),
|
|
CornerRadius = UiKit.RadWindow,
|
|
Margin = Application.Current.TryFindResource("DialogHaloMargin") is Thickness hm ? hm : new Thickness(10),
|
|
Effect = UiKit.ShadowDialog()
|
|
};
|
|
|
|
var root = new StackPanel();
|
|
|
|
var titleBar = new Border { Background = Application.Current.TryFindResource("UseDialogCaption") is true ? UiKit.Brush("TitleBarBrush") : Brushes.Transparent, Padding = new Thickness(16, 10, 16, 10) };
|
|
titleBar.MouseLeftButtonDown += (_, e) => { if (e.ButtonState == MouseButtonState.Pressed) win.DragMove(); };
|
|
if (title == "MmdPdf")
|
|
{
|
|
var wmTb = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
|
|
wmTb.Inlines.Add(new System.Windows.Documents.Run("MMD ") { FontFamily = UiKit.WordmarkFont, FontWeight = FontWeights.Normal, FontSize = 15, Foreground = R("TextBrush") });
|
|
wmTb.Inlines.Add(new System.Windows.Documents.Run("PDF") { FontFamily = UiKit.WordmarkFontPdf, FontWeight = FontWeights.Bold, FontSize = 19.5, Foreground = R("AccentLogo") });
|
|
titleBar.Child = wmTb;
|
|
}
|
|
else
|
|
{
|
|
titleBar.Child = new TextBlock { Text = title, Foreground = R("PrimaryBrush"), FontWeight = FontWeights.Bold, FontSize = 14, FontFamily = UiKit.MonoFont };
|
|
}
|
|
if (Application.Current.TryFindResource("UseDialogCaption") is true)
|
|
titleBar = DialogChrome.BuildTitleBar(win, owner, title, () => win.Close());
|
|
titleBar.Height = Application.Current.TryFindResource("DialogTitleBarHeight") is double titleHeight ? titleHeight : double.NaN;
|
|
root.Children.Add(titleBar);
|
|
|
|
root.Children.Add(new Border
|
|
{
|
|
Padding = new Thickness(20, 16, 20, 8),
|
|
Child = new TextBlock { Text = message, Foreground = R("TextBrush"), FontSize = 13, TextWrapping = TextWrapping.Wrap, MaxWidth = 560 }
|
|
});
|
|
|
|
var btnPanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right };
|
|
for (int i = 0; i < labels.Length; i++)
|
|
{
|
|
int idx = i;
|
|
var btn = UiKit.Make(labels[i], accent: i == accentIndex);
|
|
btn.Padding = new Thickness(22, 8, 22, 8);
|
|
btn.MinWidth = 96;
|
|
btn.Margin = new Thickness(8, 0, 0, 0);
|
|
btn.Click += (_, _2) => { result = idx; win.Close(); };
|
|
btnPanel.Children.Add(btn);
|
|
}
|
|
root.Children.Add(new Border { Padding = new Thickness(16, 8, 16, 16), Child = btnPanel });
|
|
|
|
var contentGrid = new Grid();
|
|
var grain = (owner as MainWindow)?.GrainTexture;
|
|
if (grain is not null)
|
|
{
|
|
double grainOpacity = Application.Current.Resources["GrainOpacity"] is double go ? go : 0.05;
|
|
contentGrid.Children.Add(new Border
|
|
{
|
|
CornerRadius = new CornerRadius(6),
|
|
IsHitTestVisible = false,
|
|
Opacity = grainOpacity,
|
|
Background = new ImageBrush(grain)
|
|
{
|
|
TileMode = TileMode.Tile,
|
|
ViewportUnits = BrushMappingMode.Absolute,
|
|
Viewport = new Rect(0, 0, 256, 256),
|
|
Stretch = Stretch.None
|
|
}
|
|
});
|
|
}
|
|
contentGrid.Children.Add(root);
|
|
win.Content = DialogChrome.WrapContent(owner, contentGrid);
|
|
win.ShowDialog();
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Like <see cref="Show"/> but with a "don't warn again" style checkbox between the message and the
|
|
/// buttons. Returns the button result and the checkbox state.
|
|
/// </summary>
|
|
// Same dialog as Show(), plus a checkbox (e.g. "Remember my choice"). Delegates to the single
|
|
// Show() implementation so there is one KillerDialog box, not a duplicate.
|
|
public static (MessageBoxResult result, bool isChecked) ShowWithCheckbox(
|
|
Window? owner,
|
|
string message,
|
|
string checkboxText,
|
|
string title = "MmdPdf",
|
|
MessageBoxButton buttons = MessageBoxButton.OKCancel,
|
|
MessageBoxResult? defaultResult = null)
|
|
{
|
|
var result = Show(owner, message, title, buttons, checkboxText: checkboxText, defaultResult: defaultResult);
|
|
return (result, _lastCheckboxChecked);
|
|
}
|
|
|
|
/// <summary>
|
|
/// KillerFind-style quit prompt (family standard): a short question with TWO opt-out
|
|
/// checkboxes stacked between the message and the buttons - "Close my open tabs"
|
|
/// (unchecked = session reopens next launch) and "Remember my choice" - plus
|
|
/// Cancel / Quit buttons where Quit is the accent + Enter default and Esc cancels.
|
|
/// Returns (confirmed, closeTabsChecked, rememberChecked).
|
|
///
|
|
/// A thin wrapper over <see cref="ShowTwoCheckPrompt"/> since the install prompt needed the
|
|
/// same two-checkbox shape. Passing check2Initial:false keeps this behavior identical to
|
|
/// what it was when the body lived here.
|
|
/// </summary>
|
|
public static (bool confirmed, bool closeTabs, bool remember) ShowQuitPrompt(
|
|
Window? owner,
|
|
string message,
|
|
string closeTabsText,
|
|
bool closeTabsInitial,
|
|
string rememberText,
|
|
string quitLabel,
|
|
string cancelLabel)
|
|
=> ShowTwoCheckPrompt(owner, message, closeTabsText, closeTabsInitial,
|
|
rememberText, false, quitLabel, cancelLabel);
|
|
|
|
/// <summary>
|
|
/// The family two-checkbox confirm: a short question, two checkboxes stacked between the
|
|
/// message and the buttons, and Cancel / confirm buttons where confirm is the accent +
|
|
/// Enter default and Esc cancels. Used by the quit prompt and by the install prompt
|
|
/// (desktop shortcut + install for all users).
|
|
/// </summary>
|
|
public static (bool confirmed, bool check1, bool check2) ShowTwoCheckPrompt(
|
|
Window? owner,
|
|
string message,
|
|
string check1Text,
|
|
bool check1Initial,
|
|
string check2Text,
|
|
bool check2Initial,
|
|
string confirmLabel,
|
|
string cancelLabel)
|
|
{
|
|
bool confirmed = false;
|
|
bool closeTabs = check1Initial;
|
|
bool remember = check2Initial;
|
|
|
|
var win = new Window { Title = "MmdPdf", Width = 380, SizeToContent = SizeToContent.Height };
|
|
// fade:false - the app's own fade-out follows immediately on confirm; two fades
|
|
// back-to-back read as lag (same reasoning as the unsaved-changes prompt).
|
|
DialogChrome.Configure(win, owner, fade: false);
|
|
|
|
var outerBorder = new Border
|
|
{
|
|
Background = R("MenuBackgroundBrush"),
|
|
BorderBrush = UiKit.Brush("DialogFrameBrush"),
|
|
BorderThickness = Application.Current.TryFindResource("DialogFrameThickness") is Thickness dft ? dft : new Thickness(1),
|
|
Padding = Application.Current.TryFindResource("DialogFramePadding") is Thickness dfp ? dfp : new Thickness(0),
|
|
CornerRadius = UiKit.RadWindow,
|
|
Margin = Application.Current.TryFindResource("DialogHaloMargin") is Thickness hm ? hm : new Thickness(10),
|
|
Effect = UiKit.ShadowDialog()
|
|
};
|
|
|
|
var root = new StackPanel();
|
|
|
|
// Title bar: the wordmark, exactly like Show()'s "MmdPdf" branch.
|
|
var titleBar = new Border
|
|
{
|
|
Background = Application.Current.TryFindResource("UseDialogCaption") is true ? UiKit.Brush("TitleBarBrush") : Brushes.Transparent,
|
|
Padding = new Thickness(16, 10, 16, 10),
|
|
CornerRadius = new CornerRadius(5, 5, 0, 0)
|
|
};
|
|
titleBar.MouseLeftButtonDown += (_, e) => { if (e.ButtonState == MouseButtonState.Pressed) win.DragMove(); };
|
|
var wm = new StackPanel { Orientation = Orientation.Horizontal };
|
|
var wmTb = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
|
|
wmTb.Inlines.Add(new System.Windows.Documents.Run("MMD ") { FontFamily = UiKit.WordmarkFont, FontWeight = FontWeights.Normal, FontSize = 15, Foreground = R("TextBrush") });
|
|
wmTb.Inlines.Add(new System.Windows.Documents.Run("PDF") { FontFamily = UiKit.WordmarkFontPdf, FontWeight = FontWeights.Bold, FontSize = 18, Foreground = R("AccentLogo") });
|
|
wm.Children.Add(wmTb);
|
|
titleBar.Child = wm;
|
|
if (Application.Current.TryFindResource("UseDialogCaption") is true)
|
|
titleBar = DialogChrome.BuildTitleBar(win, owner, "MmdPdf", () => win.Close());
|
|
titleBar.Height = Application.Current.TryFindResource("DialogTitleBarHeight") is double titleHeight ? titleHeight : double.NaN;
|
|
root.Children.Add(titleBar);
|
|
|
|
root.Children.Add(new Border
|
|
{
|
|
Padding = new Thickness(20, 16, 20, 8),
|
|
Child = new TextBlock
|
|
{
|
|
Text = message,
|
|
Foreground = R("TextBrush"),
|
|
FontSize = 13,
|
|
TextWrapping = TextWrapping.Wrap
|
|
}
|
|
});
|
|
|
|
var chk1 = UiKit.CheckBox(check1Text);
|
|
chk1.Margin = new Thickness(20, 10, 20, 0);
|
|
chk1.IsChecked = check1Initial;
|
|
chk1.Checked += (_, _2) => closeTabs = true;
|
|
chk1.Unchecked += (_, _2) => closeTabs = false;
|
|
root.Children.Add(chk1);
|
|
|
|
var chk2 = UiKit.CheckBox(check2Text);
|
|
chk2.Margin = new Thickness(20, 8, 20, 4);
|
|
chk2.IsChecked = check2Initial;
|
|
chk2.Checked += (_, _2) => remember = true;
|
|
chk2.Unchecked += (_, _2) => remember = false;
|
|
root.Children.Add(chk2);
|
|
|
|
var btnPanel = new StackPanel
|
|
{
|
|
Orientation = Orientation.Horizontal,
|
|
HorizontalAlignment = HorizontalAlignment.Right
|
|
};
|
|
var cancelBtn = UiKit.Make(cancelLabel, false);
|
|
cancelBtn.Margin = new Thickness(8, 0, 0, 0);
|
|
cancelBtn.IsCancel = true;
|
|
cancelBtn.Click += (_, _2) => win.Close();
|
|
var quitBtn = UiKit.Make(confirmLabel, true);
|
|
quitBtn.Margin = new Thickness(8, 0, 0, 0);
|
|
quitBtn.IsDefault = true;
|
|
quitBtn.Click += (_, _2) => { confirmed = true; win.Close(); };
|
|
btnPanel.Children.Add(cancelBtn);
|
|
btnPanel.Children.Add(quitBtn);
|
|
root.Children.Add(new Border
|
|
{
|
|
Padding = new Thickness(16, 12, 16, 16),
|
|
Child = btnPanel
|
|
});
|
|
|
|
// Film grain across the whole card, same as Show().
|
|
var contentGrid = new Grid();
|
|
var grain = (owner as MainWindow)?.GrainTexture;
|
|
if (grain is not null)
|
|
{
|
|
double grainOpacity = Application.Current.Resources["GrainOpacity"] is double go ? go : 0.05;
|
|
contentGrid.Children.Add(new Border
|
|
{
|
|
CornerRadius = new CornerRadius(6),
|
|
IsHitTestVisible = false,
|
|
Opacity = grainOpacity,
|
|
Background = new System.Windows.Media.ImageBrush(grain)
|
|
{
|
|
TileMode = System.Windows.Media.TileMode.Tile,
|
|
ViewportUnits = System.Windows.Media.BrushMappingMode.Absolute,
|
|
Viewport = new Rect(0, 0, 256, 256),
|
|
Stretch = System.Windows.Media.Stretch.None
|
|
}
|
|
});
|
|
}
|
|
contentGrid.Children.Add(root);
|
|
win.Content = DialogChrome.WrapContent(owner, contentGrid);
|
|
win.ShowDialog();
|
|
return (confirmed, closeTabs, remember);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Themed "Password Required" prompt: the family dialog chrome (wordmark title bar, grain,
|
|
/// red close, Esc to cancel) around a themed PasswordBox. Returns the entered password, or
|
|
/// null if the user canceled / closed the dialog.
|
|
/// </summary>
|
|
public static string? PromptPassword(Window? owner, string filename)
|
|
{
|
|
string? result = null;
|
|
|
|
var win = new Window { Width = 380, SizeToContent = SizeToContent.Height };
|
|
DialogChrome.Configure(win, owner, fade: true);
|
|
|
|
void CloseCancel() { result = null; win.Close(); }
|
|
|
|
var body = new StackPanel();
|
|
|
|
// Message: "<file>" is password protected.
|
|
var msg = new TextBlock { Foreground = R("TextBrush"), FontSize = 13, TextWrapping = TextWrapping.Wrap };
|
|
msg.Inlines.Add(new System.Windows.Documents.Run($"“{System.IO.Path.GetFileName(filename)}” ") { FontWeight = FontWeights.SemiBold });
|
|
msg.Inlines.Add(new System.Windows.Documents.Run("is password protected."));
|
|
body.Children.Add(new Border { Padding = new Thickness(20, 4, 20, 10), Child = msg });
|
|
|
|
var pw = UiKit.PasswordField();
|
|
body.Children.Add(new Border { Padding = new Thickness(20, 0, 20, 4), Child = pw });
|
|
|
|
var openBtn = UiKit.Make("Open", accent: true);
|
|
openBtn.IsDefault = true;
|
|
openBtn.Click += (_, _2) => { result = pw.Password; win.Close(); };
|
|
var cancelBtn = UiKit.Make("Cancel", accent: false);
|
|
cancelBtn.IsCancel = true;
|
|
cancelBtn.Click += (_, _2) => CloseCancel();
|
|
body.Children.Add(new Border { Padding = new Thickness(16, 12, 16, 16), Child = UiKit.ButtonRow(openBtn, cancelBtn) });
|
|
|
|
// Enter anywhere in the field submits.
|
|
pw.KeyDown += (_, e) => { if (e.Key == Key.Enter) { result = pw.Password; win.Close(); } };
|
|
|
|
win.Content = DialogChrome.Frame(win, owner, "MmdPdf", CloseCancel, body);
|
|
win.Loaded += (_, _2) => pw.Focus();
|
|
win.ShowDialog();
|
|
return result;
|
|
}
|
|
|
|
}
|
|
}
|