using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using PdfSharpCore.Pdf; namespace MmdPdf { /// /// The window's half of the viewer bridge. Read this alongside /// Controls/PdfViewer.Bridge.cs, which is the other end of every line here. /// /// TWO DIRECTIONS, and they are kept apart on purpose: /// /// INWARD - accessors the viewer reads. MainWindow's own members are private and a control /// in another namespace cannot see them. Rather than widen ~40 fields in place and /// scatter `internal` through fifteen files, each is exposed once, here, under a /// name that says it is a bridge rather than an ordinary member. The private /// fields stay private, so nothing else in the app gains reach by accident. /// /// OUTWARD - focused-pane routing for shared toolbar and keyboard commands. Per-viewer /// editing and document state stays on PdfViewer; only window-owned commands /// cross this boundary. /// /// XAML handlers remain on MainWindow because WPF resolves them against the XAML root. /// public partial class MainWindow { // ══ INWARD: chrome the viewer reads ═════════════════════════════════════════════════ // PageList is not here - x:Name fields are generated internal, so the control already // sees it. These four are hand-declared private fields assigned from FindName, so they // are not. /// The window's ONE PageList selection delegate. Handed out rather than rebuilt /// because SyncCurrentPageTo detaches and reattaches it - a method group would make a new /// delegate per call and the -= would quietly remove nothing. internal SelectionChangedEventHandler PageListSelectionHandler => _pageListSelectionHandler ??= PageList_SelectionChanged; private SelectionChangedEventHandler? _pageListSelectionHandler; // ══ INWARD: per-document state (group B - goes when the viewer owns its session) ═════ // Settable: the xref-repair path in Annotations.cs, which lives in the viewer, reopens the // document and re-points the temp file. // Reads OUT of the focused pane rather than exposing a window field: the session list // belongs to the viewer. Callers still spell it `_active` - see the alias below. internal Controls.PdfViewer.DocumentSession? ActiveSession => ActiveViewer.ActiveSessionRef; private Controls.PdfViewer.DocumentSession? _active => ActiveViewer.ActiveSessionRef; // Reads OUT of the control rather than exposing a window field: the link-rect map belongs // to the viewer, alongside Links.cs. ContextMenu.cs and FileOperations.cs still call it by // this name. private Dictionary> _continuousLinks => ActiveViewer.ContinuousLinks; // Live gesture state, shared with the annotation and crop tools that have not moved yet. // ══ OUTWARD: the viewer's members, under the names the window already calls ══════════ // Signatures mirror the originals exactly, defaults included, so no call site changed. private System.Threading.Tasks.Task RenderContinuousPages(int centerPage) => ActiveViewer.RenderContinuousPages(centerPage); private void BootstrapDocumentView(int initialPage, bool autoFit, bool restoreFitMode = false) => ActiveViewer.BootstrapDocumentView(initialPage, autoFit, restoreFitMode); private void RefreshPageView(int pageIndex) => ActiveViewer.RefreshPageView(pageIndex); private void ScrollContinuousToPage(int pageIndex) => ActiveViewer.ScrollContinuousToPage(pageIndex); private void StartRerenderTimer() => ActiveViewer.StartRerenderTimer(); private void SetZoom(double level) => ActiveViewer.SetZoom(level); private void SetTrueZoom(double trueZoom) => ActiveViewer.SetTrueZoom(trueZoom); private void GridZoomStep(bool zoomOut) => ActiveViewer.GridZoomStep(zoomOut); private double GridZoomForN(int n) => ActiveViewer.GridZoomForN(n); private double DisplayZoomPct() => ActiveViewer.DisplayZoomPct(); private void SyncZoomBox() => ActiveViewer.SyncZoomBox(); private void FitToWidth(bool lite = false) => ActiveViewer.FitToWidth(lite); private void FitToPage(bool lite = false) => ActiveViewer.FitToPage(lite); private void SetViewMode(ViewMode mode) => ActiveViewer.SetViewMode(mode); private void SelectViewMode(ViewMode mode) => ActiveViewer.SelectViewMode(mode); private void ApplyViewMode(ViewMode mode) => ActiveViewer.ApplyViewMode(mode); private ViewMode? _pendingViewMode { get => ActiveViewer.PendingViewMode; set => ActiveViewer.PendingViewMode = value; } private bool NavigatePageStep(int direction) => ActiveViewer.NavigatePageStep(direction); private void NavigatePageByWheel(int delta) => ActiveViewer.NavigatePageByWheel(delta); private int _gridColumns { get => ActiveViewer.GridColumns; set => ActiveViewer.GridColumns = value; } private void BuildPrimaryTile() => ActiveViewer.BuildPrimaryTile(); private void PagePreviewPanel_SizeChanged(object sender, SizeChangedEventArgs e) => ActiveViewer.PagePreviewPanel_SizeChanged(sender, e); // Bound from MainWindow.xaml (the zoom toolbar stays on the window) and from // ContextMenu.cs, so these three cannot simply live on the control. private void ZoomIn_Click(object sender, RoutedEventArgs e) => ActiveViewer.ZoomIn_Click(sender, e); private void ZoomOut_Click(object sender, RoutedEventArgs e) => ActiveViewer.ZoomOut_Click(sender, e); // ACTIVEVIEWER, like every other stub in this file - this one said `Viewer` (pane A, // hardcoded) and was the split's cross-zoom bug: FocusPane(B) -> SyncZoomBox writes the // shared box -> SelectionChanged -> this stub ran PANE A's handler, which FitToWidth'd // pane A against pane B's document (proven by zoomtrace, 2026-08-01). // NULL-CONDITIONAL, and it must stay that way. ZoomBox declares // (MainWindow.xaml), so SelectionChanged fires // while InitializeComponent is still walking the tree - ActiveViewer is not assigned until // InitSplitPanes runs after it, so the `?.` no-ops the mid-parse fire exactly like the old // `_zoomBox?.SelectedItem` guard did. Click handlers do not need this - a click cannot // happen mid-parse. private void ZoomBox_SelectionChanged(object sender, SelectionChangedEventArgs e) => ActiveViewer?.ZoomBox_SelectionChanged(sender, e); // Wheel over the zoom dropdown nudges the zoom like Ctrl+scroll. Null-conditional for the // same mid-parse reason as SelectionChanged above. private void ZoomBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e) => ActiveViewer?.ZoomBoxWheel(e); } }