feat: rebrand to MMD PDF, single corporate theme, and remove original text under an edit

- Services/PdfRedactText.cs: strip text whose origin falls inside a CoverAnnotation
  from the page content stream at save time, hooked into PdfBurn.DrawAnnotationsIntoDoc.
  Fixes edited values staying recoverable by text extraction.
- Themes/MMD.xaml replaces all thirteen themes; picker and accent strip removed; no dark mode.
- Rename KillerPDF -> MMD PDF across code, resources, packaging and locale strings; new icon.
- Remove the upstream author credit and the in-app install button.
This commit is contained in:
2026-08-27 07:37:09 +02:00
parent 532485a830
commit 6610acfa44
230 changed files with 9362 additions and 11508 deletions
+6 -6
View File
@@ -3,9 +3,9 @@ using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using KillerPDF.Features;
using MmdPdf.Features;
namespace KillerPDF
namespace MmdPdf
{
/// <summary>
/// The About overlay's window half: the IAboutHost implementation that maps the controller's
@@ -13,9 +13,9 @@ namespace KillerPDF
/// handlers. All the logic - signature, hashing, update check, self-update - lives in
/// <see cref="AboutController"/>.
///
/// NOTE: this stays "namespace KillerPDF" rather than KillerPDF.Shell, because it is a partial
/// NOTE: this stays "namespace MmdPdf" rather than MmdPdf.Shell, because it is a partial
/// of MainWindow and every partial of a class must share one namespace. It moves to
/// KillerPDF.Shell when MainWindow itself does.
/// MmdPdf.Shell when MainWindow itself does.
/// </summary>
public partial class MainWindow : IAboutHost
{
@@ -123,7 +123,7 @@ namespace KillerPDF
FontSize = 27.3,
Foreground = Res("AccentLogo")
});
hl.Click += (_, _) => AboutController.OpenUrl("https://killerpdf.net");
hl.Click += (_, _) => AboutController.OpenUrl("https://baobab-ts.com");
AboutLogoBlock.Inlines.Add(hl);
// The shadow copy mirrors the real runs exactly (sizes, weights, fonts), so the blur
@@ -176,7 +176,7 @@ namespace KillerPDF
AddText(pre);
AboutTaglineBlock.Inlines.Add(
AccentLink("Killer Tools", () => AboutController.OpenUrl("https://killertools.net")));
AccentLink("Killer Tools", () => AboutController.OpenUrl("https://baobab-ts.com")));
AddText(suf);
}
+2 -2
View File
@@ -17,10 +17,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
+1 -1
View File
@@ -4,7 +4,7 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace KillerPDF
namespace MmdPdf
{
// App-wide accessibility size, ported from KillerNotes: a LayoutTransform scale on the
// chrome (toolbar row, sidebar, tab strip) grows or shrinks the UI crisply -
+3 -3
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -318,7 +318,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_DuplicateFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_DuplicateFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
+3 -3
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -140,7 +140,7 @@ namespace KillerPDF
private static void HideBusyOverlay(Border overlay)
=> (overlay.Parent as Panel)?.Children.Remove(overlay);
// RenderToPng lives in Services/BitmapHelpers.cs (KillerUI refactor), with the other
// RenderToPng lives in Services/BitmapHelpers.cs (MmdPdfUI refactor), with the other
// raw-bitmap helpers.
}
}
+4 -4
View File
@@ -17,10 +17,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -154,7 +154,7 @@ namespace KillerPDF
{
var ask = KillerDialog.Show(this,
string.Format(Loc("Str_Dlg_RepairAsk"), System.IO.Path.GetFileName(path)),
"KillerPDF", MessageBoxButton.YesNo, MessageBoxImage.Warning);
"MmdPdf", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (ask != MessageBoxResult.Yes) return null;
var busy = ShowBusyOverlay(Loc("Str_Busy_Repairing"));
@@ -174,7 +174,7 @@ namespace KillerPDF
if (repaired is null)
KillerDialog.Show(this,
$"\"{System.IO.Path.GetFileName(path)}\" could not be repaired.",
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
return repaired;
}
finally { HideBusyOverlay(busy); }
+4 -4
View File
@@ -2,9 +2,9 @@ using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using KillerPDF.Services;
using MmdPdf.Services;
namespace KillerPDF
namespace MmdPdf
{
/// <summary>
/// Single-instance entry points: App forwards a second launch's file path here rather than
@@ -88,8 +88,8 @@ namespace KillerPDF
{
try { File.Delete(temp); } catch { }
KillerDialog.Show(this,
$"KillerPDF could not open the browser PDF.\n\n{ex.Message}",
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Warning);
$"MmdPdf could not open the browser PDF.\n\n{ex.Message}",
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
+32 -32
View File
@@ -16,13 +16,13 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
// CliParsePageRange moved out with the CLI runner but is used here too (Export Images'
// range box); its encoder sibling is now Services/BitmapHelpers.EncodeJpeg.
using static KillerPDF.Features.CliRunner;
using static MmdPdf.Features.CliRunner;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -123,14 +123,14 @@ namespace KillerPDF
SetStatus(string.Format(Loc("Str_OpenedReadOnlyXRef"), System.IO.Path.GetFileName(path), _doc.PageCount));
KillerDialog.Show(this,
$"\"{System.IO.Path.GetFileName(path)}\" has a non-standard structure and was opened read-only.\n\nEditing, saving, and some other features may not work correctly.",
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Warning);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch
{
// ReadOnly also failed - offer to repair.
var result = KillerDialog.Show(this,
$"This PDF has a damaged structure and couldn't be opened.\n\nWould you like KillerPDF to attempt a repair? A repaired copy will be created - the original file will not be changed.\n\nNote: repaired files may be missing bookmarks, forms, and other interactive features.",
"KillerPDF", MessageBoxButton.YesNo, MessageBoxImage.Warning);
$"This PDF has a damaged structure and couldn't be opened.\n\nWould you like MmdPdf to attempt a repair? A repaired copy will be created - the original file will not be changed.\n\nNote: repaired files may be missing bookmarks, forms, and other interactive features.",
"MmdPdf", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
TryRepairAndOpen(srcPath);
}
@@ -150,8 +150,8 @@ namespace KillerPDF
// we can't classify the damage, but the PDFium-based repair often recovers it anyway, so
// offer the repair rather than just failing outright.
var result = KillerDialog.Show(this,
"This PDF couldn't be opened - its structure may be damaged.\n\nWould you like KillerPDF to attempt a repair? A repaired copy will be created - the original file will not be changed.\n\nNote: repaired files may be missing bookmarks, forms, and other interactive features.",
"KillerPDF", MessageBoxButton.YesNo, MessageBoxImage.Warning);
"This PDF couldn't be opened - its structure may be damaged.\n\nWould you like MmdPdf to attempt a repair? A repaired copy will be created - the original file will not be changed.\n\nNote: repaired files may be missing bookmarks, forms, and other interactive features.",
"MmdPdf", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
TryRepairAndOpen(srcPath); // sets _asyncOpenPending and finalizes the tab itself
}
@@ -213,7 +213,7 @@ namespace KillerPDF
// Themed "Password Required" prompt (KillerDialog): family dialog chrome + themed PasswordBox.
private string? PromptForPassword(string filename) => KillerDialog.PromptPassword(this, filename);
// ALL direct PDFium P/Invoke lives in Services/PdfiumInterop.cs (KillerUI refactor) -
// ALL direct PDFium P/Invoke lives in Services/PdfiumInterop.cs (MmdPdfUI refactor) -
// one class, one lock (Docnet's), so the thread-safety discipline stays auditable.
// PdfFileHasEncryption and TryImportRepairToPath live in Services/PdfImport.cs.
@@ -234,7 +234,7 @@ namespace KillerPDF
bool raster = false;
// Strategy 0 (#103): lossless PDFium re-save. PDFium's tolerant parser recovers
// broken xref tables (including the dangling /Outlines entry older KillerPDF
// broken xref tables (including the dangling /Outlines entry older MmdPdf
// builds wrote) and rewrites a clean file preserving EVERYTHING - forms,
// bookmarks, text. The import-copy below drops the document-level AcroForm,
// which is what used to turn a repaired fillable form into a flat one.
@@ -267,7 +267,7 @@ namespace KillerPDF
_asyncOpenPending = false;
KillerDialog.Show(this,
"Repair failed - the file is too severely damaged to recover.\n\nTry opening the original in a different application (Adobe Acrobat, browsers) which may have additional recovery options.",
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
@@ -284,13 +284,13 @@ namespace KillerPDF
raster
? string.Format(Loc("Str_Dlg_RepairedRaster"), System.IO.Path.GetFileName(path))
: string.Format(Loc("Str_Dlg_Repaired"), System.IO.Path.GetFileName(path)),
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.None);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.None);
}
catch (Exception ex)
{
HideBusyOverlay(busy);
_asyncOpenPending = false;
KillerDialog.Show(this, Loc("Str_Err_RepairFailed") + "\n" + ex.Message, "KillerPDF",
KillerDialog.Show(this, Loc("Str_Err_RepairFailed") + "\n" + ex.Message, "MmdPdf",
MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
@@ -333,7 +333,7 @@ namespace KillerPDF
HideBusyOverlay(busy);
_asyncOpenPending = false;
KillerDialog.Show(this, Loc("Str_Err_OpenProtected") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
EndCancellableOp();
}
}
@@ -363,7 +363,7 @@ namespace KillerPDF
{
var res = KillerDialog.Show(this,
Loc("Str_Dlg_UnsavedClose"),
"KillerPDF", MessageBoxButton.YesNo, MessageBoxImage.Warning);
"MmdPdf", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (res != MessageBoxResult.Yes) return;
}
_doc.Close();
@@ -445,7 +445,7 @@ namespace KillerPDF
{
AbortTabLoad(target, prev, createdNew);
KillerDialog.Show(this, Loc("Str_Err_NewDocFailed") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -478,7 +478,7 @@ namespace KillerPDF
var item = MakeMenuItem(System.IO.Path.GetFileName(path), (_, _) =>
{
if (System.IO.File.Exists(path)) OpenInNewTab(path);
else KillerDialog.Show(this, Loc("Str_Err_FileNotFound") + "\n" + path, "KillerPDF",
else KillerDialog.Show(this, Loc("Str_Err_FileNotFound") + "\n" + path, "MmdPdf",
MessageBoxButton.OK, MessageBoxImage.Warning);
});
item.ToolTip = path;
@@ -562,7 +562,7 @@ namespace KillerPDF
}
// The file-type icon lookup (per-extension cache + SHGetFileInfo) lives in
// Services/ShellIcons.cs - the same shape the KillerUI file picker uses.
// Services/ShellIcons.cs - the same shape the MmdPdfUI file picker uses.
// Fills the empty-state "Recent" list with clickable filenames (hidden when there are none).
// The start screen belongs to a PANE, so a pane filling its own list has to name itself: the
@@ -714,7 +714,7 @@ namespace KillerPDF
// through ActiveViewer. Clicking pane B's recents opened the file in pane A.
FocusPane(pane);
if (System.IO.File.Exists(path)) OpenInNewTab(path);
else KillerDialog.Show(this, Loc("Str_Err_FileNotFound") + "\n" + path, "KillerPDF",
else KillerDialog.Show(this, Loc("Str_Err_FileNotFound") + "\n" + path, "MmdPdf",
MessageBoxButton.OK, MessageBoxImage.Warning);
};
list.Items.Add(row);
@@ -800,15 +800,15 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_MergeFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_MergeFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// The named-destination helpers (BuildNamedDestMap, RewriteNamedDestLinks and their
// private walkers) live in Services/PdfImport.cs (KillerUI refactor).
// private walkers) live in Services/PdfImport.cs (MmdPdfUI refactor).
// DerefItemStatic, RectNum and the pre-save scrubs live in Services/PdfScrub.cs
// (KillerUI refactor) - pure functions shared by the GUI saves, TempReload and the CLI.
// (MmdPdfUI refactor) - pure functions shared by the GUI saves, TempReload and the CLI.
// ============================================================
// Adobe page-size guard
@@ -817,8 +817,8 @@ namespace KillerPDF
// Adobe Reader only displays pages whose sides are 3-14400 points; anything outside
// that range shows "The dimensions of this page are out-of-range" and renders blank.
// Such pages usually come from images with broken DPI metadata turned into a PDF (by
// KillerPDF 1.6.1 and earlier, or by other tools). PDFium renders any size, so the
// file looks normal in KillerPDF and only fails in Adobe.
// MmdPdf 1.6.1 and earlier, or by other tools). PDFium renders any size, so the
// file looks normal in MmdPdf and only fails in Adobe.
// Min/MaxAdobePageDim live in Services/PdfImport.cs (shared with the image importer).
private static bool PageOutOfAdobeRange(PdfPage p)
@@ -838,7 +838,7 @@ namespace KillerPDF
if (PageOutOfAdobeRange(_doc.Pages[i])) bad++;
if (bad == 0) return;
var res = KillerDialog.Show(this, string.Format(Loc("Str_Dlg_PageOutOfRange"), bad),
"KillerPDF", MessageBoxButton.YesNo, MessageBoxImage.Warning);
"MmdPdf", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (res != MessageBoxResult.Yes) return;
for (int i = 0; i < _doc.PageCount; i++)
if (PageOutOfAdobeRange(_doc.Pages[i]))
@@ -971,7 +971,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_SaveFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_SaveFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -1088,7 +1088,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_SaveFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_SaveFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -1171,7 +1171,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
try { KillerDialog.Show(this, Loc("Str_Err_FlattenFailed") + "\n" + ex.GetType().Name + ": " + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error); }
try { KillerDialog.Show(this, Loc("Str_Err_FlattenFailed") + "\n" + ex.GetType().Name + ": " + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error); }
catch { /* dialog failed; overlay still removed in finally */ }
}
finally
@@ -1207,7 +1207,7 @@ namespace KillerPDF
if (selected is null)
{
KillerDialog.Show(this, rangeErr.Length > 0 ? rangeErr : Loc("Str_InvalidRange"),
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Warning);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
}
@@ -1284,7 +1284,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_ExportFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_ExportFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
@@ -1430,7 +1430,7 @@ namespace KillerPDF
// Open the preview window immediately. Pages rasterize on a background thread and
// stream in via SetRenderedPage, so the window appears at once and the app stays
// responsive on large files. WPF's OS PrintDialog can't show a preview, so KillerPDF
// responsive on large files. WPF's OS PrintDialog can't show a preview, so MmdPdf
// renders it and drives printing itself.
string renderPath = printPath;
string? cleanup = tempFlattened;
@@ -1481,7 +1481,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
try { KillerDialog.Show(this, Loc("Str_Err_PrintFailed") + "\n" + ex.GetType().Name + ": " + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error); }
try { KillerDialog.Show(this, Loc("Str_Err_PrintFailed") + "\n" + ex.GetType().Name + ": " + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error); }
catch { }
}
}
+2 -2
View File
@@ -7,7 +7,7 @@ using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -131,7 +131,7 @@ namespace KillerPDF
var b = CurrentMonitorBoundsDip();
Topmost = true;
// #215: Topmost exists only to cover the always-on-top taskbar while KillerPDF is
// #215: Topmost exists only to cover the always-on-top taskbar while MmdPdf is
// the ACTIVE window. Held unconditionally, it sat over every other program the user
// switched to. Yield it on deactivate, take it back on return - browser behavior.
Deactivated += FsYieldTopmost;
+12 -12
View File
@@ -10,9 +10,9 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
namespace KillerPDF
namespace MmdPdf
{
// Import images as a PDF, and compress the current PDF to a .zip. Kept in its own partial-class
// file rather than the MainWindow monolith. User-facing strings go through Loc() (keys live in
@@ -51,7 +51,7 @@ namespace KillerPDF
{
AbortTabLoad(target, prev, createdNew);
KillerDialog.Show(this, Loc("Str_Err_ImportFailed") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -73,7 +73,7 @@ namespace KillerPDF
return outPath;
}
// AddImagePagesFromFile and IsPdfPath live in Services/PdfImport.cs (KillerUI refactor).
// AddImagePagesFromFile and IsPdfPath live in Services/PdfImport.cs (MmdPdfUI refactor).
// ----- Drag/drop of folders, archives, and multiple files ----------------------------
@@ -107,7 +107,7 @@ namespace KillerPDF
{
CleanupDirs(tempDirs);
KillerDialog.Show(this, Loc("Str_Err_ImportFailed") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
@@ -125,7 +125,7 @@ namespace KillerPDF
{
var proceed = KillerDialog.Show(this,
string.Format(Loc("Str_Drop_TooMany"), found.Count, MaxDropFiles),
"KillerPDF", MessageBoxButton.OKCancel);
"MmdPdf", MessageBoxButton.OKCancel);
if (proceed != MessageBoxResult.OK) { CleanupDirs(tempDirs); return; }
found = found.GetRange(0, MaxDropFiles);
}
@@ -164,7 +164,7 @@ namespace KillerPDF
if (found.Count > 30)
{
var ok = KillerDialog.Show(this, string.Format(Loc("Str_Drop_ManyTabs"), found.Count),
"KillerPDF", MessageBoxButton.OKCancel);
"MmdPdf", MessageBoxButton.OKCancel);
if (ok != MessageBoxResult.OK) return;
}
foreach (var f in found)
@@ -207,7 +207,7 @@ namespace KillerPDF
HideBusyOverlay(busy);
AbortTabLoad(target, prev, createdNew);
KillerDialog.Show(this, Loc("Str_Err_ImportFailed") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
@@ -235,7 +235,7 @@ namespace KillerPDF
{
AbortTabLoad(target, prev, createdNew);
KillerDialog.Show(this, Loc("Str_Err_ImportFailed") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -285,7 +285,7 @@ namespace KillerPDF
private static string? ExtractZipToTemp(string zipPath)
{
string dir = Path.Combine(Path.GetTempPath(), "KillerPDF-zip-" + Guid.NewGuid().ToString("N")[..8]);
string dir = Path.Combine(Path.GetTempPath(), "MmdPdf-zip-" + Guid.NewGuid().ToString("N")[..8]);
try { Directory.CreateDirectory(dir); ZipFile.ExtractToDirectory(zipPath, dir); return dir; }
catch { try { Directory.Delete(dir, true); } catch { } return null; }
}
@@ -305,7 +305,7 @@ namespace KillerPDF
if (_isDirty || string.IsNullOrEmpty(_originalFile) || !File.Exists(_originalFile))
{
var ask = KillerDialog.Show(this, Loc("Str_Dlg_SaveBeforeZip"),
"KillerPDF", MessageBoxButton.OKCancel);
"MmdPdf", MessageBoxButton.OKCancel);
if (ask != MessageBoxResult.OK) return;
SaveInPlace();
if (_isDirty || string.IsNullOrEmpty(_originalFile) || !File.Exists(_originalFile))
@@ -337,7 +337,7 @@ namespace KillerPDF
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_ZipFailed") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
+3 -3
View File
@@ -5,7 +5,7 @@ using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace KillerPDF
namespace MmdPdf
{
// ============================================================
// Visual keyboard for the shortcuts overlay.
@@ -19,7 +19,7 @@ namespace KillerPDF
public partial class MainWindow
{
// KbLayer, the binding table and its types live in Services/ShortcutTable.cs, which is free
// of WPF so KillerPDF.Tests can link it.
// of WPF so MmdPdf.Tests can link it.
private KbLayer _kbLayer = KbLayer.Base;
private bool _kbBuilt;
private TextBlock? _kbDetail;
@@ -187,7 +187,7 @@ namespace KillerPDF
key.SetResourceReference(Border.CornerRadiusProperty, "ControlCornerRadius");
key.SetResourceReference(Border.BackgroundProperty, "KeyboardKeyBrush");
key.SetResourceReference(Border.BorderBrushProperty, "CardBorderBrush");
// Hover: the keycap lifts a few pixels, like the cards on the killertools.net front page.
// Hover: the keycap lifts a few pixels, like the cards on the baobab-ts.com front page.
var lift = new TranslateTransform();
key.RenderTransform = lift;
string keyId = id;
+4 -4
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -147,7 +147,7 @@ namespace KillerPDF
{
// A cancellable long operation (OCR, repair) is running behind the busy overlay - offer to
// cancel it instead of letting Escape fall through to the app-exit handler below.
if (KillerDialog.Show(this, string.Format(Loc("Str_Dlg_CancelBusy"), _busyOpLabel), "KillerPDF",
if (KillerDialog.Show(this, string.Format(Loc("Str_Dlg_CancelBusy"), _busyOpLabel), "MmdPdf",
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
_busyCts?.Cancel();
e.Handled = true;
@@ -698,7 +698,7 @@ namespace KillerPDF
// Opens the online help / how-to page in the user's default browser.
private void OnlineHelp_Click(object sender, RoutedEventArgs e)
{
try { Process.Start(new ProcessStartInfo("https://killerpdf.net/help.html") { UseShellExecute = true }); }
try { Process.Start(new ProcessStartInfo("https://baobab-ts.com/help.html") { UseShellExecute = true }); }
catch { }
}
}
+8 -8
View File
@@ -4,10 +4,10 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using KillerPDF.Features;
using KillerPDF.Services;
using MmdPdf.Features;
using MmdPdf.Services;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow : IOcrHost
{
@@ -193,13 +193,13 @@ namespace KillerPDF
OcrLanguages.TryDeleteFile(dest + ".part");
if (ct.IsCancellationRequested) SetStatus(string.Format(Loc("Str_St_LangCanceled"), name));
else KillerDialog.Show(this, string.Format(Loc("Str_Dlg_DownloadTimeout"), name),
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (Exception ex)
{
HideBusyOverlay(busy);
OcrLanguages.TryDeleteFile(dest + ".part");
KillerDialog.Show(this, string.Format(Loc("Str_Err_LangDataFailed"), name) + "\n" + ex.Message, "KillerPDF",
KillerDialog.Show(this, string.Format(Loc("Str_Err_LangDataFailed"), name) + "\n" + ex.Message, "MmdPdf",
MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
@@ -266,7 +266,7 @@ namespace KillerPDF
catch (Exception ex)
{
HideBusyOverlay(busy);
KillerDialog.Show(this, Loc("Str_Err_HqDownloadFailed") + "\n" + ex.Message, "KillerPDF",
KillerDialog.Show(this, Loc("Str_Err_HqDownloadFailed") + "\n" + ex.Message, "MmdPdf",
MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
@@ -292,7 +292,7 @@ namespace KillerPDF
string names = string.Join(", ", missing.ConvertAll(OcrLanguages.NameForCode));
var choice = KillerDialog.Show(this,
string.Format(Loc("Str_Dlg_LangDownloadAsk"), names),
"KillerPDF", MessageBoxButton.OKCancel, MessageBoxImage.Information);
"MmdPdf", MessageBoxButton.OKCancel, MessageBoxImage.Information);
if (choice != MessageBoxResult.OK) return false;
var ct = BeginCancellableOp(Loc("Str_Op_LangDownload"));
@@ -325,7 +325,7 @@ namespace KillerPDF
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_ModelDownloadFailed") + "\n" + ex.Message,
"KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
"MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
finally
+7 -7
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -81,7 +81,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_SplitFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_SplitFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -91,7 +91,7 @@ namespace KillerPDF
var doc = _doc;
var selected = PageList.SelectedItems;
if (selected.Count == 0) { KillerDialog.Show(this, Loc("Str_Dlg_SelectDelete")); return; }
var result = KillerDialog.Show(this, selected.Count == 1 ? Loc("Str_Dlg_DeletePage1") : string.Format(Loc("Str_Dlg_DeletePagesN"), selected.Count), "KillerPDF",
var result = KillerDialog.Show(this, selected.Count == 1 ? Loc("Str_Dlg_DeletePage1") : string.Format(Loc("Str_Dlg_DeletePagesN"), selected.Count), "MmdPdf",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result != MessageBoxResult.Yes) return;
try
@@ -105,7 +105,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_DeleteFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_DeleteFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -124,7 +124,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_InsertFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_InsertFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -143,7 +143,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_AddPageFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_AddPageFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
+2 -2
View File
@@ -2,9 +2,9 @@ using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using KillerPDF.Controls;
using MmdPdf.Controls;
namespace KillerPDF
namespace MmdPdf
{
/// <summary>
/// Moving a document tab from one pane to the other. Partial of MainWindow.
+3 -23
View File
@@ -3,7 +3,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace KillerPDF
namespace MmdPdf
{
/// <summary>
/// The rail's flyout buttons (family order, locked 2026-07-30: app-specific toggles, then
@@ -20,7 +20,6 @@ namespace KillerPDF
private void RailLang_Click(object sender, RoutedEventArgs e) => ToggleRailFlyout(LangFlyout);
private void RailTheme_Click(object sender, RoutedEventArgs e) => ToggleRailFlyout(ThemeFlyout);
private void RailView_Click(object sender, RoutedEventArgs e) => ToggleRailFlyout(ViewFlyout);
@@ -49,14 +48,14 @@ namespace KillerPDF
SyncPickerState();
}
private void ToggleRailFlyout(ContextMenu menu)
{
if (menu.IsOpen) { menu.IsOpen = false; return; }
// The theme strip always faces the document. Mirroring the two columns also makes its
// width animation grow outward from the rail on either side of the window.
if (menu == ThemeFlyout)
SyncThemeFlyoutSide();
// Radios and accent dots reflect live state before the card shows - the same single
// sync the Settings panel runs on open.
@@ -79,24 +78,5 @@ namespace KillerPDF
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut }
});
}
private void SyncThemeFlyoutSide()
{
if (ThemePickerLayout is null || ThemeSubmenu is null || AccentStripHost is null ||
AccentStripDivider is null || AccentStrip is null)
return;
Grid.SetColumn(ThemeSubmenu, _sidebarRight ? 1 : 0);
Grid.SetColumn(AccentStripHost, _sidebarRight ? 0 : 1);
AccentStripDivider.HorizontalAlignment = _sidebarRight
? HorizontalAlignment.Right
: HorizontalAlignment.Left;
AccentStrip.Margin = _sidebarRight
? new Thickness(2, 6, 7, 6)
: new Thickness(7, 6, 2, 6);
ThemePickerLayout.Margin = _sidebarRight
? new Thickness(3, 10, 12, 10)
: new Thickness(12, 10, 3, 10);
}
}
}
+3 -3
View File
@@ -9,9 +9,9 @@ using Docnet.Core.Models;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -150,7 +150,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, string.Format(Loc("Str_Tf_Failed"), ex.Message), "KillerPDF",
KillerDialog.Show(this, string.Format(Loc("Str_Tf_Failed"), ex.Message), "MmdPdf",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
+3 -3
View File
@@ -16,11 +16,11 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Features;
using KillerPDF.Services;
using MmdPdf.Features;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow : ISearchHost
{
+64 -212
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -31,48 +31,6 @@ namespace KillerPDF
// Syncs every picker's radios and accent dots to live state before a flyout shows -
// exactly ONE sync implementation, shared by all the rail flyouts (Shell/RailFlyouts.cs).
private void SyncPickerState()
{
var cur = ThemeManager.Current;
ThemeDarkRadio.IsChecked = cur == Theme.Dark;
ThemeLightRadio.IsChecked = cur == Theme.Light;
ThemeHCRadio.IsChecked = cur == Theme.Black;
Theme98SERadio.IsChecked = cur == Theme.SE98;
ThemeBloodRadio.IsChecked = cur == Theme.Blood;
ThemeGreedRadio.IsChecked = cur == Theme.Greed;
ThemeCyanoticRadio.IsChecked = cur == Theme.Cyanotic;
ThemeEctoplasmRadio.IsChecked = cur == Theme.Ectoplasm;
ThemeDecayRadio.IsChecked = cur == Theme.Decay;
ThemeMourningRadio.IsChecked = cur == Theme.Mourning;
ThemeSepulchreRadio.IsChecked = cur == Theme.Sepulchre;
ThemeDeliriumRadio.IsChecked = cur == Theme.Delirium;
ThemeMalaiseRadio.IsChecked = cur == Theme.Malaise;
UpdateAccentStrip(animate: false);
// Sync language picker
var curLoc = KillerPDF.Services.LocaleManager.Current;
LangEnRadio.IsChecked = curLoc == KillerPDF.Services.Locale.EnUS;
LangCsRadio.IsChecked = curLoc == KillerPDF.Services.Locale.CsCZ;
LangEsRadio.IsChecked = curLoc == KillerPDF.Services.Locale.Es;
LangFrRadio.IsChecked = curLoc == KillerPDF.Services.Locale.Fr;
LangZhTWRadio.IsChecked = curLoc == KillerPDF.Services.Locale.ZhTW;
LangZhCNRadio.IsChecked = curLoc == KillerPDF.Services.Locale.ZhCN;
LangBnRadio.IsChecked = curLoc == KillerPDF.Services.Locale.Bn;
LangTrRadio.IsChecked = curLoc == KillerPDF.Services.Locale.TrTR;
LangDeRadio.IsChecked = curLoc == KillerPDF.Services.Locale.De;
LangJaRadio.IsChecked = curLoc == KillerPDF.Services.Locale.JaJP;
LangPlRadio.IsChecked = curLoc == KillerPDF.Services.Locale.PlPL;
LangHuRadio.IsChecked = curLoc == KillerPDF.Services.Locale.HuHU;
// Sync view mode radios. Against the PENDING mode while a fade-wrapped switch is in
// flight (_viewMode lags until the fade-out lands), so wheel-cycling with the flyout
// open moves the checkmark in step instead of one notch behind.
var vm = _pendingViewMode ?? _viewMode;
ViewSingleRadio.IsChecked = vm == ViewMode.Single;
ViewContinuousRadio.IsChecked = vm == ViewMode.Continuous;
ViewTwoPageRadio.IsChecked = vm == ViewMode.TwoPage;
ViewGridRadio.IsChecked = vm == ViewMode.Grid;
// (The toolbar picker lives on the toolbar's own right-click menu, and the sidebar
// side on the sidebar's - both build their checks fresh on open, nothing to sync here.)
}
// ── Quick fade in/out for the full-window overlay panels (Shortcuts/About) ──
private static void FadeOverlayIn(UIElement el)
@@ -340,176 +298,41 @@ namespace KillerPDF
RefreshOpenAnnotationBars();
}
private void ThemeDarkRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Dark);
private void ThemeLightRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Light);
private void ThemeHCRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Black);
private void Theme98SERadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.SE98);
private void ThemeBloodRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Blood);
private void ThemeGreedRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Greed);
private void ThemeCyanoticRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Cyanotic);
private void ThemeEctoplasmRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Ectoplasm);
private void ThemeDecayRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Decay);
private void ThemeMourningRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Mourning);
private void ThemeSepulchreRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Sepulchre);
private void ThemeDeliriumRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Delirium);
private void ThemeMalaiseRadio_Checked(object sender, RoutedEventArgs e) => SelectTheme(Theme.Malaise);
private void SelectTheme(Theme theme)
{
bool wasOpen = ThemeFlyout is not null && ThemeFlyout.IsOpen;
ThemeManager.Apply(theme);
UpdateAccentStrip(animate: true);
// Intentionally leave the flyout open so the user can try another theme right away
// without reopening the submenu. The theme swap's side effects (tab strip rebuild,
// tool re-select) can still knock the popup closed behind our back, so check once
// layout has settled and quietly reopen it in place if that happened.
if (wasOpen)
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background,
(Action)(() => { if (ThemeFlyout is not null && !ThemeFlyout.IsOpen) ThemeFlyout.IsOpen = true; }));
}
// #199 (Ryokoxx's design): ONE vertical strip of six swatches beside the theme list,
// repainted per family, instead of a row under each accented theme. The list height is
// identical for every theme, so the bottom-pinned card never moves; only the strip's
// width animates, growing rightward from the pinned left edge. Per-family accent memory
// in ThemeManager is untouched.
private void AccentStripDot_Click(object sender, MouseButtonEventArgs e) => HandleAccentDot(sender, _stripFamily);
private void HandleAccentDot(object sender, Theme family)
{
if (sender is not FrameworkElement fe || fe.Tag is not string tag) return;
if (!Enum.TryParse<DarkAccent>(tag, out var accent)) return;
ThemeManager.ApplyAccent(family, accent); // persists for that family; reapplies if active
RingAccentStrip();
}
// Per-family swatch colors, in each family's display order (moved here from the four
// retired XAML accent rows; the strip repaints these onto its six shared dots).
private static readonly (DarkAccent Accent, string Hex)[] DarkStripColors =
[(DarkAccent.Red, "#DD504B"), (DarkAccent.Orange, "#E8962C"), (DarkAccent.Green, "#1EA54C"),
(DarkAccent.Teal, "#1FB8A8"), (DarkAccent.Blue, "#4580D9"), (DarkAccent.Purple, "#B982E3")];
private static readonly (DarkAccent Accent, string Hex)[] LightStripColors =
[(DarkAccent.Red, "#931A1A"), (DarkAccent.Orange, "#C7710F"), (DarkAccent.Green, "#1B5E20"),
(DarkAccent.Teal, "#0D827E"), (DarkAccent.Blue, "#18608E"), (DarkAccent.Purple, "#5A1690")];
private static readonly (DarkAccent Accent, string Hex)[] BlackStripColors =
[(DarkAccent.Red, "#FF2929"), (DarkAccent.Orange, "#FF910A"), (DarkAccent.Green, "#00FF66"),
(DarkAccent.Teal, "#0AFFE7"), (DarkAccent.Blue, "#298DFF"), (DarkAccent.Purple, "#B829FF")];
private static readonly (DarkAccent Accent, string Hex)[] SE98StripColors =
[(DarkAccent.Red, "#800040"), (DarkAccent.Orange, "#A05000"), (DarkAccent.Green, "#006000"),
(DarkAccent.Teal, "#008080"), (DarkAccent.Blue, "#000080"), (DarkAccent.Purple, "#5A376E")];
private static readonly (DarkAccent Accent, string Hex)[] DarkStripColors = [];
private static readonly (DarkAccent Accent, string Hex)[] LightStripColors = [];
private static readonly (DarkAccent Accent, string Hex)[] BlackStripColors = [];
private static readonly (DarkAccent Accent, string Hex)[] SE98StripColors = [];
private static (DarkAccent Accent, string Hex)[] StripColorsFor(Theme family) => family switch
{
Theme.Light => LightStripColors,
Theme.Black => BlackStripColors,
Theme.SE98 => SE98StripColors,
_ => DarkStripColors,
};
private Theme _stripFamily = Theme.Dark; // the family the strip's dots currently show
private bool _stripOpen;
private const double AccentStripWidth = 39; // 1px rule + 10 gap + 26 swatch + 2 air
private const double AccentStripSlideMs = 180;
private Border[] StripDots =>
[AccentStripDot0, AccentStripDot1, AccentStripDot2, AccentStripDot3, AccentStripDot4, AccentStripDot5];
private void PopulateAccentStrip(Theme family)
{
var colors = StripColorsFor(family);
var dots = StripDots;
for (int i = 0; i < dots.Length; i++)
{
var c = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(colors[i].Hex);
dots[i].Background = new System.Windows.Media.SolidColorBrush(c);
dots[i].Tag = colors[i].Accent.ToString();
}
_stripFamily = family;
RingAccentStrip();
}
private Border[] StripDots => [];
// Ring the strip's selected swatch for the family it is showing.
private void RingAccentStrip()
{
if (AccentStrip is null) return;
var ring = (System.Windows.Media.Brush)FindResource("TextBrush");
var chosen = _stripFamily switch
{
Theme.Light => ThemeManager.LightAccentChoice,
Theme.Black => ThemeManager.BlackAccentChoice,
Theme.SE98 => ThemeManager.SE98AccentChoice,
_ => ThemeManager.DarkAccentChoice,
};
foreach (var dot in StripDots)
{
bool sel = dot.Tag is string t && Enum.TryParse<DarkAccent>(t, out var a) && a == chosen;
dot.BorderBrush = sel ? ring : System.Windows.Media.Brushes.Transparent;
}
}
private void UpdateAccentStrip(bool animate)
{
var cur = ThemeManager.Current;
bool show = cur is Theme.Dark or Theme.Light or Theme.Black or Theme.SE98;
if (show)
{
if (animate && _stripOpen && _stripFamily != cur)
{
// Accented -> accented: the strip stays open and the swatches CROSSFADE.
// Re-running the open animation here snapped the strip shut and back.
var target = cur;
var fadeOut = new System.Windows.Media.Animation.DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(90));
fadeOut.Completed += (_, __) =>
{
PopulateAccentStrip(target);
AccentStrip.BeginAnimation(OpacityProperty,
new System.Windows.Media.Animation.DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(90)));
};
AccentStrip.BeginAnimation(OpacityProperty, fadeOut);
}
else PopulateAccentStrip(cur);
}
SlideAccentStrip(show, animate);
}
// Width slide, eased - one element animates now, so the old linear lockstep constraint
// that kept two rows summing to a constant height no longer applies.
private void SlideAccentStrip(bool show, bool animate)
{
if (show == _stripOpen && animate) return; // already there; crossfade handles content
_stripOpen = show;
AccentStripHost.BeginAnimation(WidthProperty, null); // drop any in-flight slide
if (!animate) { AccentStripHost.Width = show ? AccentStripWidth : 0; return; }
double from = double.IsNaN(AccentStripHost.Width) ? AccentStripHost.ActualWidth : AccentStripHost.Width;
var anim = new System.Windows.Media.Animation.DoubleAnimation(from, show ? AccentStripWidth : 0,
TimeSpan.FromMilliseconds(AccentStripSlideMs))
{
EasingFunction = new System.Windows.Media.Animation.QuadraticEase
{ EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut }
};
anim.Completed += (_, __) =>
{
AccentStripHost.BeginAnimation(WidthProperty, null);
AccentStripHost.Width = _stripOpen ? AccentStripWidth : 0;
};
AccentStripHost.BeginAnimation(WidthProperty, anim);
}
// Localized display name for each theme, shown on the picker row.
private string ThemeDisplayName(Theme t) => t switch
{
Theme.Light => Loc("Str_Theme_Light"),
Theme.Black => Loc("Str_Theme_Black"),
Theme.SE98 => Loc("Str_Theme_98SE"),
Theme.Blood => Loc("Str_Theme_Blood"),
Theme.Greed => Loc("Str_Theme_Greed"),
Theme.Cyanotic => Loc("Str_Theme_Cyanotic"),
Theme.Ectoplasm => Loc("Str_Theme_Ectoplasm"),
Theme.Decay => Loc("Str_Theme_Decay"),
Theme.Mourning => Loc("Str_Theme_Mourning"),
Theme.Sepulchre => Loc("Str_Theme_Sepulchre"),
Theme.Delirium => Loc("Str_Theme_Delirium"),
Theme.Malaise => Loc("Str_Theme_Malaise"),
_ => Loc("Str_Theme_Dark"),
};
@@ -518,7 +341,7 @@ namespace KillerPDF
// DynamicResource strings. Called once from the window ctor; the event only ever fires
// when the app was started with --lang-file.
internal void HookExternalLangReload()
=> KillerPDF.Services.LocaleManager.ExternalReloaded += () =>
=> MmdPdf.Services.LocaleManager.ExternalReloaded += () =>
{
ApplyToolNumberTooltips();
BuildToolbarMenu();
@@ -527,22 +350,22 @@ namespace KillerPDF
RefreshOpenAnnotationBars();
};
private void LangEnRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.EnUS);
private void LangCsRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.CsCZ);
private void LangEsRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.Es);
private void LangFrRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.Fr);
private void LangZhTWRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.ZhTW);
private void LangZhCNRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.ZhCN);
private void LangBnRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.Bn);
private void LangTrRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.TrTR);
private void LangDeRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.De);
private void LangJaRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.JaJP);
private void LangPlRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.PlPL);
private void LangHuRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(KillerPDF.Services.Locale.HuHU);
private void LangEnRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.EnUS);
private void LangCsRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.CsCZ);
private void LangEsRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.Es);
private void LangFrRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.Fr);
private void LangZhTWRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.ZhTW);
private void LangZhCNRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.ZhCN);
private void LangBnRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.Bn);
private void LangTrRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.TrTR);
private void LangDeRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.De);
private void LangJaRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.JaJP);
private void LangPlRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.PlPL);
private void LangHuRadio_Checked(object sender, RoutedEventArgs e) => SelectLocale(MmdPdf.Services.Locale.HuHU);
private void SelectLocale(KillerPDF.Services.Locale loc)
private void SelectLocale(MmdPdf.Services.Locale loc)
{
KillerPDF.Services.LocaleManager.Apply(loc);
MmdPdf.Services.LocaleManager.Apply(loc);
ApplyToolNumberTooltips(); // re-append the numbers to the now-localized tool tooltips
BuildToolbarMenu(); // the toolbar right-click picker's items carry Loc() captions
LangFlyout.IsOpen = false; // a pick closes the rail flyout, like the accordion used to collapse
@@ -592,9 +415,9 @@ namespace KillerPDF
RefreshOpenAnnotationBars(active);
}
private void RefreshOpenAnnotationBars(KillerPDF.Controls.PdfViewer viewer)
private void RefreshOpenAnnotationBars(MmdPdf.Controls.PdfViewer viewer)
{
((KillerPDF.Features.IViewerHost)this).RunWithViewerContext(viewer, () =>
((MmdPdf.Features.IViewerHost)this).RunWithViewerContext(viewer, () =>
{
var tool = _annotBarTool;
if (tool == EditTool.Text)
@@ -638,17 +461,17 @@ namespace KillerPDF
}
// Native name (autonym) for each language, shown in the picker regardless of UI locale.
private static string LangDisplayName(KillerPDF.Services.Locale loc) => loc switch
private static string LangDisplayName(MmdPdf.Services.Locale loc) => loc switch
{
KillerPDF.Services.Locale.CsCZ => "Čeština",
KillerPDF.Services.Locale.Es => "Español",
KillerPDF.Services.Locale.Fr => "Français",
KillerPDF.Services.Locale.ZhTW => "中文 (繁體)",
KillerPDF.Services.Locale.ZhCN => "中文 (简体)",
KillerPDF.Services.Locale.Bn => "বাংলা",
KillerPDF.Services.Locale.TrTR => "Türkçe",
KillerPDF.Services.Locale.De => "Deutsch",
KillerPDF.Services.Locale.JaJP => "日本語",
MmdPdf.Services.Locale.CsCZ => "Čeština",
MmdPdf.Services.Locale.Es => "Español",
MmdPdf.Services.Locale.Fr => "Français",
MmdPdf.Services.Locale.ZhTW => "中文 (繁體)",
MmdPdf.Services.Locale.ZhCN => "中文 (简体)",
MmdPdf.Services.Locale.Bn => "বাংলা",
MmdPdf.Services.Locale.TrTR => "Türkçe",
MmdPdf.Services.Locale.De => "Deutsch",
MmdPdf.Services.Locale.JaJP => "日本語",
_ => "English",
};
@@ -670,7 +493,7 @@ namespace KillerPDF
// ── Toolbar appearance (right-click picker on the bar) ────────────
// Hover tooltips stay on in every mode, so the text modes are about preference, not
// discoverability.
// TWO AXES, NOT ONE (family standard, 2026-07-30; KillerUI/Shell/ToolbarStyle.cs is
// TWO AXES, NOT ONE (family standard, 2026-07-30; MmdPdfUI/Shell/ToolbarStyle.cs is
// the reference). The old five-way ToolbarStyle could not express "large icons WITH text" -
// icon size and text placement were never one axis, they only looked like one. The old
// enum survives solely so an existing install's saved setting migrates (InitToolbarStyle).
@@ -1192,5 +1015,34 @@ namespace KillerPDF
{
OverflowChevron.IsChecked = false; // close the flyout after a choice is made
}
private void SyncPickerState()
{
var cur = ThemeManager.Current;
// Sync language picker
var curLoc = MmdPdf.Services.LocaleManager.Current;
LangEnRadio.IsChecked = curLoc == MmdPdf.Services.Locale.EnUS;
LangCsRadio.IsChecked = curLoc == MmdPdf.Services.Locale.CsCZ;
LangEsRadio.IsChecked = curLoc == MmdPdf.Services.Locale.Es;
LangFrRadio.IsChecked = curLoc == MmdPdf.Services.Locale.Fr;
LangZhTWRadio.IsChecked = curLoc == MmdPdf.Services.Locale.ZhTW;
LangZhCNRadio.IsChecked = curLoc == MmdPdf.Services.Locale.ZhCN;
LangBnRadio.IsChecked = curLoc == MmdPdf.Services.Locale.Bn;
LangTrRadio.IsChecked = curLoc == MmdPdf.Services.Locale.TrTR;
LangDeRadio.IsChecked = curLoc == MmdPdf.Services.Locale.De;
LangJaRadio.IsChecked = curLoc == MmdPdf.Services.Locale.JaJP;
LangPlRadio.IsChecked = curLoc == MmdPdf.Services.Locale.PlPL;
LangHuRadio.IsChecked = curLoc == MmdPdf.Services.Locale.HuHU;
// Sync view mode radios. Against the PENDING mode while a fade-wrapped switch is in
// flight (_viewMode lags until the fade-out lands), so wheel-cycling with the flyout
// open moves the checkmark in step instead of one notch behind.
var vm = _pendingViewMode ?? _viewMode;
ViewSingleRadio.IsChecked = vm == ViewMode.Single;
ViewContinuousRadio.IsChecked = vm == ViewMode.Continuous;
ViewTwoPageRadio.IsChecked = vm == ViewMode.TwoPage;
ViewGridRadio.IsChecked = vm == ViewMode.Grid;
// (The toolbar picker lives on the toolbar's own right-click menu, and the sidebar
// side on the sidebar's - both build their checks fresh on open, nothing to sync here.)
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
+1 -1
View File
@@ -3,7 +3,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace KillerPDF
namespace MmdPdf
{
// ============================================================
// Keyboard-shortcuts overlay: the LIST view.
+108 -108
View File
@@ -4,7 +4,7 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace KillerPDF
namespace MmdPdf
{
// Configurable sidebar placement (left or right). The layout uses three columns in
// MainContentGrid: one sized sidebar column, a 6px splitter, and a star document column.
@@ -12,113 +12,6 @@ namespace KillerPDF
// existing collapse / resize logic keeps working unchanged.
public partial class MainWindow
{
private void ApplySidebarSide()
{
// SplitHost, NOT Viewer. Since the split landed, the element that sits in
// MainContentGrid is the SPLIT HOST - Viewer is one of its children. Setting
// Grid.Column on Viewer therefore moved pane A into SplitHost's column 2, which is
// PaneBCol and is ZERO WIDTH while unsplit, so the document rendered into a pane with
// no width and the whole content area went blank. Same for the margin below: the 8px
// gutter to the window edge belongs to the host, not to one pane.
//
// Do NOT look up TabStripBorder / TabScroll here: those elements live inside PdfViewer,
// so FindName cannot see them (a UserControl is its own namescope and FindName returns
// null SILENTLY). They would only re-span the band across the splitter column, which is
// meaningless when each strip lives inside its own pane and spans it exactly.
if (FindName("SidebarCol") is not ColumnDefinition sidebarColDef ||
FindName("DocCol") is not ColumnDefinition docColDef ||
FindName("SidebarOuterGrid") is not Grid sbOuter ||
FindName("SidebarBorder") is not Border sbContent ||
FindName("SidebarToggleStrip") is not Border sbToggle ||
SplitHost is not FrameworkElement docPane ||
FindName("SbContentCol") is not ColumnDefinition sbContentCol ||
FindName("SbToggleCol") is not ColumnDefinition sbToggleCol)
return;
// Carry the sized column's current width across a flip (24px when collapsed, else the
// user's width). A star length means it isn't the sized column yet, so fall back.
GridLength sized = _sidebarCollapsed
? new GridLength(SbPx(24))
: (_sidebarCol != null && _sidebarCol.Width.GridUnitType == GridUnitType.Pixel)
? _sidebarCol.Width
: new GridLength(SbPx(180));
double maxW = SbPx(_sidebarShowingOutlines ? SidebarMaxOutlines : SidebarMaxPages);
if (!_sidebarRight)
{
// Sidebar on the LEFT (column 0); document fills column 2.
sidebarColDef.MinWidth = SbPx(_sidebarCollapsed ? 24 : SidebarMinOpen); sidebarColDef.MaxWidth = maxW; sidebarColDef.Width = sized;
docColDef.MinWidth = 0; docColDef.MaxWidth = double.PositiveInfinity;
docColDef.Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(sbOuter, 0);
Grid.SetColumn(docPane, 2);
_sidebarCol = sidebarColDef;
// Toggle strip faces the document: right edge of the sidebar.
sbContentCol.Width = new GridLength(1, GridUnitType.Star);
sbToggleCol.Width = new GridLength(24, GridUnitType.Pixel);
Grid.SetColumn(sbContent, 0);
Grid.SetColumn(sbToggle, 1);
}
else
{
// Sidebar on the RIGHT (column 2); document fills column 0.
docColDef.MinWidth = SbPx(_sidebarCollapsed ? 24 : SidebarMinOpen); docColDef.MaxWidth = maxW; docColDef.Width = sized;
sidebarColDef.MinWidth = 0; sidebarColDef.MaxWidth = double.PositiveInfinity;
sidebarColDef.Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(sbOuter, 2);
Grid.SetColumn(docPane, 0);
_sidebarCol = docColDef;
// Toggle strip faces the document: left edge of the sidebar (inner column 0). The
// inner column defs are fixed in position, so size them by position, not by name.
sbContentCol.Width = new GridLength(24, GridUnitType.Pixel); // inner col 0 -> toggle
sbToggleCol.Width = new GridLength(1, GridUnitType.Star); // inner col 1 -> content
Grid.SetColumn(sbToggle, 0);
Grid.SetColumn(sbContent, 1);
}
// The splitter's edge-line and the SidebarShadow gradient were both handled here. The
// splitter draws a single centered line now, which is symmetric and so needs no side
// handling, and the fake elevation gradient is gone - DocPaneBorder casts a real
// PaneShadow on all four sides. (2026-07-31.)
// The DocTopAccent / DocBottomAccent repositioning was here. Those two 1px rules are
// gone with the squared layout - the card carries its own border on all four sides now,
// so there is nothing left to bridge to the toolbar and footer. (2026-07-31.)
// The document card's 8px inset always sits on its OUTER edge - the window side, away
// from the splitter - so the gap reads as a margin off the window rather than a gutter
// between the pane and the sidebar. Full screen clears the margin, so leave it alone
// there; ApplyFullScreen restores it from this same helper on exit.
// The grip rides the CONTENT column and faces the rail, so it always sits on the list's
// inner lip: right edge with the sidebar on the left, left edge with it on the right.
if (FindName("SidebarSplitter") is Thumb grip)
{
Grid.SetColumn(grip, _sidebarRight ? 1 : 0);
grip.HorizontalAlignment = _sidebarRight ? HorizontalAlignment.Left
: HorizontalAlignment.Right;
}
// The shadow caster is inside the PdfViewer control, so moving the control moves both -
// one less thing that can drift out of alignment than a separate grid child that has to
// be moved in step with the pane.
if (!_fullScreen)
{
// docPane is SplitHost, so this one margin now insets BOTH panes from the window
// edge together, which is what the 8px gutter always meant.
docPane.Margin = DocPaneInsetMargin();
// No tab-band or TabBarRing margins here. Those exist only to stop a window-level
// band overhanging the card's rounded outer corner, where the band runs the full
// width while the card is inset 8px. Each strip is inside its own pane and spans
// exactly that pane, so the band and the card share an edge by construction.
}
UpdateSidebarToggleGlyph();
SyncThemeFlyoutSide();
UpdateTabStripFade();
// The column swap repositions the document pane; re-anchor the footer shadow once layout
// settles (TransformToVisual needs the final positions).
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Loaded, (Action)UpdateFooterFade);
}
// Document on the right (sidebar left) -> 8px inset on the right; mirrored when the sidebar
// moves. Top and bottom stay 0: PaneShadow is Direction 270 and draws outside the element's
@@ -339,5 +232,112 @@ namespace KillerPDF
}
return null;
}
private void ApplySidebarSide()
{
// SplitHost, NOT Viewer. Since the split landed, the element that sits in
// MainContentGrid is the SPLIT HOST - Viewer is one of its children. Setting
// Grid.Column on Viewer therefore moved pane A into SplitHost's column 2, which is
// PaneBCol and is ZERO WIDTH while unsplit, so the document rendered into a pane with
// no width and the whole content area went blank. Same for the margin below: the 8px
// gutter to the window edge belongs to the host, not to one pane.
//
// Do NOT look up TabStripBorder / TabScroll here: those elements live inside PdfViewer,
// so FindName cannot see them (a UserControl is its own namescope and FindName returns
// null SILENTLY). They would only re-span the band across the splitter column, which is
// meaningless when each strip lives inside its own pane and spans it exactly.
if (FindName("SidebarCol") is not ColumnDefinition sidebarColDef ||
FindName("DocCol") is not ColumnDefinition docColDef ||
FindName("SidebarOuterGrid") is not Grid sbOuter ||
FindName("SidebarBorder") is not Border sbContent ||
FindName("SidebarToggleStrip") is not Border sbToggle ||
SplitHost is not FrameworkElement docPane ||
FindName("SbContentCol") is not ColumnDefinition sbContentCol ||
FindName("SbToggleCol") is not ColumnDefinition sbToggleCol)
return;
// Carry the sized column's current width across a flip (24px when collapsed, else the
// user's width). A star length means it isn't the sized column yet, so fall back.
GridLength sized = _sidebarCollapsed
? new GridLength(SbPx(24))
: (_sidebarCol != null && _sidebarCol.Width.GridUnitType == GridUnitType.Pixel)
? _sidebarCol.Width
: new GridLength(SbPx(180));
double maxW = SbPx(_sidebarShowingOutlines ? SidebarMaxOutlines : SidebarMaxPages);
if (!_sidebarRight)
{
// Sidebar on the LEFT (column 0); document fills column 2.
sidebarColDef.MinWidth = SbPx(_sidebarCollapsed ? 24 : SidebarMinOpen); sidebarColDef.MaxWidth = maxW; sidebarColDef.Width = sized;
docColDef.MinWidth = 0; docColDef.MaxWidth = double.PositiveInfinity;
docColDef.Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(sbOuter, 0);
Grid.SetColumn(docPane, 2);
_sidebarCol = sidebarColDef;
// Toggle strip faces the document: right edge of the sidebar.
sbContentCol.Width = new GridLength(1, GridUnitType.Star);
sbToggleCol.Width = new GridLength(24, GridUnitType.Pixel);
Grid.SetColumn(sbContent, 0);
Grid.SetColumn(sbToggle, 1);
}
else
{
// Sidebar on the RIGHT (column 2); document fills column 0.
docColDef.MinWidth = SbPx(_sidebarCollapsed ? 24 : SidebarMinOpen); docColDef.MaxWidth = maxW; docColDef.Width = sized;
sidebarColDef.MinWidth = 0; sidebarColDef.MaxWidth = double.PositiveInfinity;
sidebarColDef.Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(sbOuter, 2);
Grid.SetColumn(docPane, 0);
_sidebarCol = docColDef;
// Toggle strip faces the document: left edge of the sidebar (inner column 0). The
// inner column defs are fixed in position, so size them by position, not by name.
sbContentCol.Width = new GridLength(24, GridUnitType.Pixel); // inner col 0 -> toggle
sbToggleCol.Width = new GridLength(1, GridUnitType.Star); // inner col 1 -> content
Grid.SetColumn(sbToggle, 0);
Grid.SetColumn(sbContent, 1);
}
// The splitter's edge-line and the SidebarShadow gradient were both handled here. The
// splitter draws a single centered line now, which is symmetric and so needs no side
// handling, and the fake elevation gradient is gone - DocPaneBorder casts a real
// PaneShadow on all four sides. (2026-07-31.)
// The DocTopAccent / DocBottomAccent repositioning was here. Those two 1px rules are
// gone with the squared layout - the card carries its own border on all four sides now,
// so there is nothing left to bridge to the toolbar and footer. (2026-07-31.)
// The document card's 8px inset always sits on its OUTER edge - the window side, away
// from the splitter - so the gap reads as a margin off the window rather than a gutter
// between the pane and the sidebar. Full screen clears the margin, so leave it alone
// there; ApplyFullScreen restores it from this same helper on exit.
// The grip rides the CONTENT column and faces the rail, so it always sits on the list's
// inner lip: right edge with the sidebar on the left, left edge with it on the right.
if (FindName("SidebarSplitter") is Thumb grip)
{
Grid.SetColumn(grip, _sidebarRight ? 1 : 0);
grip.HorizontalAlignment = _sidebarRight ? HorizontalAlignment.Left
: HorizontalAlignment.Right;
}
// The shadow caster is inside the PdfViewer control, so moving the control moves both -
// one less thing that can drift out of alignment than a separate grid child that has to
// be moved in step with the pane.
if (!_fullScreen)
{
// docPane is SplitHost, so this one margin now insets BOTH panes from the window
// edge together, which is what the 8px gutter always meant.
docPane.Margin = DocPaneInsetMargin();
// No tab-band or TabBarRing margins here. Those exist only to stop a window-level
// band overhanging the card's rounded outer corner, where the band runs the full
// width while the card is inset 8px. Each strip is inside its own pane and spans
// exactly that pane, so the band and the card share an edge by construction.
}
UpdateSidebarToggleGlyph();
UpdateTabStripFade();
// The column swap repositions the document pane; re-anchor the footer shadow once layout
// settles (TransformToVisual needs the final positions).
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Loaded, (Action)UpdateFooterFade);
}
}
}
+3 -3
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -657,7 +657,7 @@ namespace KillerPDF
}
// CountOutlines, RemoveOutlineRecursive and the stale-link-key scrubs live in
// Services/PdfOutlines.cs (KillerUI refactor) - pure functions over the outline tree.
// Services/PdfOutlines.cs (MmdPdfUI refactor) - pure functions over the outline tree.
/// <summary>Rebuilds the outline panel after an edit, keeping every branch's expand/collapse
/// state (the PdfOutline objects survive the rebuild, so they key the state).</summary>
+5 -5
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -789,7 +789,7 @@ namespace KillerPDF
{
if (strokes.Count == 0)
{
KillerDialog.Show(this, Loc("Str_Dlg_DrawSignatureFirst"), "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Warning);
KillerDialog.Show(this, Loc("Str_Dlg_DrawSignatureFirst"), "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
@@ -829,7 +829,7 @@ namespace KillerPDF
btnPanel.Margin = new Thickness(12, 4, 12, 12);
contentArea.Children.Add(btnPanel);
win.Content = DialogChrome.Frame(win, this, "KillerPDF - " + Loc("Str_Sig_Create"), () => win.Close(), contentArea);
win.Content = DialogChrome.Frame(win, this, "MmdPdf - " + Loc("Str_Sig_Create"), () => win.Close(), contentArea);
win.ShowDialog();
}
@@ -872,7 +872,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_ImportImageFailed") + "\n" + ex.Message, "KillerPDF",
KillerDialog.Show(this, Loc("Str_Err_ImportImageFailed") + "\n" + ex.Message, "MmdPdf",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
+2 -2
View File
@@ -3,9 +3,9 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using KillerPDF.Controls;
using MmdPdf.Controls;
namespace KillerPDF
namespace MmdPdf
{
/// <summary>
/// Two document panes side by side in one window.
+2 -2
View File
@@ -5,9 +5,9 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using PdfSharpCore.Drawing;
using KillerPDF.Services;
using MmdPdf.Services;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
+5 -5
View File
@@ -6,7 +6,7 @@ using System.Windows.Interop;
using System.Windows.Media.Animation;
// ============================================================
// THEMED WINDOWS SYSTEM MENU (from KillerUI/Shell/SystemMenu.cs)
// THEMED WINDOWS SYSTEM MENU (from MmdPdfUI/Shell/SystemMenu.cs)
//
// WindowChrome turns the title bar into a real native caption, which is what buys free snap,
// drag and double-click-maximize - but it also means Windows answers a caption right-click
@@ -19,11 +19,11 @@ using System.Windows.Media.Animation;
// WM_SYSCOMMAND the native menu would have sent, so behavior is identical - including Move and
// Size, which hand off to Windows' own modal drag loops.
//
// KillerPDF adaptations: HTCAPTION already lives in Shell/WindowChrome.cs (not redeclared
// MmdPdf adaptations: HTCAPTION already lives in Shell/WindowChrome.cs (not redeclared
// here), the menu comes from MakeThemedMenu() so its TextOptions match the app's other
// code-built menus, and the kit's Anim.FadeIn is inlined (KillerPDF carries no Anim class).
// code-built menus, and the kit's Anim.FadeIn is inlined (MmdPdf carries no Anim class).
// ============================================================
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -90,7 +90,7 @@ namespace KillerPDF
};
}
// KillerPDF keeps its implicit ContextMenu/MenuItem styles in MainWindow.xaml's
// MmdPdf keeps its implicit ContextMenu/MenuItem styles in MainWindow.xaml's
// WINDOW resources (not App.xaml), and an Absolute-placement menu has no inheritance
// context to find them through - it rendered stock white. PlacementTarget supplies
// that context; with PlacementMode.Absolute it does not affect position.
+2 -2
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
+4 -4
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -680,7 +680,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_LoadImageFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_LoadImageFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
@@ -764,7 +764,7 @@ namespace KillerPDF
}
catch (Exception ex)
{
KillerDialog.Show(this, Loc("Str_Err_PasteFailed") + "\n" + ex.Message, "KillerPDF", MessageBoxButton.OK, MessageBoxImage.Error);
KillerDialog.Show(this, Loc("Str_Err_PasteFailed") + "\n" + ex.Message, "MmdPdf", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
+2 -2
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
+3 -3
View File
@@ -16,10 +16,10 @@ using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
namespace MmdPdf
{
public partial class MainWindow
{
@@ -289,7 +289,7 @@ namespace KillerPDF
bool updating = machineInstallExists || userInstallExists;
// Two checkboxes, matching Killendar and KillerShell: the desktop shortcut (on by
// default, as it always was) and the all-users install. If an all-users copy already
// exists, keep that scope selected: KillerPDF deliberately supports one installed
// exists, keep that scope selected: MmdPdf deliberately supports one installed
// copy, not competing Program Files and per-user installations.
var (confirmed, wantDesktop, allUsers) = KillerDialog.ShowTwoCheckPrompt(this,
Loc(machineInstallExists ? "Str_Dlg_UpdateMachineMsg" :