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 Docnet.Core;
using Docnet.Core.Models;
using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using MmdPdf.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace MmdPdf
{
public partial class MainWindow
{
// ============================================================
// Sidebar outline/bookmark panel
// ============================================================
private void SidebarPagesTab_Click(object sender, RoutedEventArgs e) => SwitchSidebarToPagesTab();
private void SidebarOutlinesTab_Click(object sender, RoutedEventArgs e) => SwitchSidebarToOutlinesTab();
private const double SidebarMaxPages = 234; // stops when the 200px-capped thumbnail fills (200 + margins + scrollbar)
private const double SidebarMaxOutlines = 480;
private const double SidebarMinOpen = 120; // narrowest readable width before labels/header clip
private void SwitchSidebarToPagesTab()
{
_sidebarShowingOutlines = false;
PageList.Visibility = Visibility.Visible;
OutlineScrollViewer.Visibility = Visibility.Collapsed;
PageControlsRow.Visibility = _doc != null ? Visibility.Visible : Visibility.Collapsed; // no empty box when nothing is open
SidebarPagesTab.Foreground = (Brush)FindResource("PrimaryBrush");
SidebarOutlinesTab.Foreground = (Brush)FindResource("MutedTextBrush");
// Save current outlines width before snapping back to pages.
if (!_sidebarCollapsed && _sidebarCol.ActualWidth > 0)
_savedOutlinesWidth = Math.Min(_sidebarCol.ActualWidth, SbPx(SidebarMaxOutlines));
SidebarSplitter.IsEnabled = true; // pages are resizable too now (drag the splitter)
_sidebarCol.MaxWidth = SbPx(SidebarMaxPages);
if (!_sidebarCollapsed)
{
double target = _savedPagesWidth;
Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Render,
(Action)(() => _sidebarCol.Width = new GridLength(target)));
}
}
private void SwitchSidebarToOutlinesTab()
{
// Save current pages width, then restore (or auto-fit) the outlines width.
if (!_sidebarCollapsed && _sidebarCol.ActualWidth > 0)
_savedPagesWidth = Math.Min(_sidebarCol.ActualWidth, SbPx(SidebarMaxPages));
_sidebarShowingOutlines = true;
PageList.Visibility = Visibility.Collapsed;
OutlineScrollViewer.Visibility = Visibility.Visible;
PageControlsRow.Visibility = Visibility.Collapsed;
SidebarPagesTab.Foreground = (Brush)FindResource("MutedTextBrush");
SidebarOutlinesTab.Foreground = (Brush)FindResource("PrimaryBrush");
SidebarSplitter.IsEnabled = true;
_sidebarCol.MaxWidth = SbPx(SidebarMaxOutlines);
if (!_sidebarCollapsed)
{
if (!_outlinesFitted)
Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Render,
(Action)AutoFitOutlineWidth);
else
{
double target = _savedOutlinesWidth;
Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Render,
(Action)(() => _sidebarCol.Width = new GridLength(target)));
}
}
}
///
/// Sizes the sidebar to fit the widest outline item by measuring each item's
/// text width via FormattedText plus its indentation depth.
///
private void AutoFitOutlineWidth()
{
if (_sidebarCollapsed) return;
var typeface = new Typeface(
OutlineTree.FontFamily, OutlineTree.FontStyle,
OutlineTree.FontWeight, OutlineTree.FontStretch);
double em = OutlineTree.FontSize;
double max = 0;
void Walk(ItemCollection items, int depth)
{
foreach (TreeViewItem node in items)
{
if (node.Tag is not OutlineNodeRef) continue; // ghost add-row: no text to measure
var ft = new System.Windows.Media.FormattedText(
node.Header?.ToString() ?? string.Empty,
System.Globalization.CultureInfo.CurrentUICulture,
FlowDirection.LeftToRight, typeface, em, Brushes.White,
/*pixelsPerDip*/ 1.0);
// 19 px indent per level + 19 px toggle + text + 12 px item padding
double w = depth * 19.0 + 19.0 + ft.Width + 12.0;
if (w > max) max = w;
if (node.Items.Count > 0)
Walk(node.Items, depth + 1);
}
}
Walk(OutlineTree.Items, 0);
// TreeView outer padding (8 px) + sidebar margins + scrollbar gutter (~36 px).
// Measured widths are logical (the tree lives in the scaled grid); the column
// is screen px, so convert.
double target = SbPx(Math.Max(160.0, Math.Min(max + 44.0, SidebarMaxOutlines)));
_savedOutlinesWidth = target;
_outlinesFitted = true;
_sidebarCol.Width = new GridLength(target);
}
private void OutlineTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs