using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Animation; namespace MmdPdf { /// /// The rail's flyout buttons (family order, locked 2026-07-30: app-specific toggles, then /// ? / language / theme, theme bottom-most) and their flyouts. The theme, language and view /// pickers all moved here OUT of the retired Settings panel - one implementation each, as /// family-standard flyouts: ContextMenus with FlyoutCard/FlyoutGrain chrome, opened against /// the content pane's bottom-left corner via FlyoutPlacement so they never cover the rail, /// the footer, or the desktop. Their radio/dot sync is SyncPickerState (SettingsPanel.cs). /// public partial class MainWindow { // The shortcuts ? is the strip's ORIGINAL button (ShortcutHelp_Click) - it just moved // into the family slot above language; no second implementation was added. private void RailLang_Click(object sender, RoutedEventArgs e) => ToggleRailFlyout(LangFlyout); private void RailView_Click(object sender, RoutedEventArgs e) => ToggleRailFlyout(ViewFlyout); // Rolling the wheel over the view-mode rail button steps through the modes without // opening the flyout: up = next, down = previous (2026-07-31 - down-as-next felt // reversed). F9 jogs forward from the keyboard. private void RailView_Wheel(object sender, System.Windows.Input.MouseWheelEventArgs e) { CycleViewMode(forward: e.Delta > 0); e.Handled = true; } /// Steps to the neighboring view mode, wrapping at the ends. Cycle order is the /// enum order: Single -> Continuous -> TwoPage -> Grid. Syncs the flyout radios in case /// the flyout is open while the wheel or F9 drives the change. private void CycleViewMode(bool forward = true) { var modes = (ViewMode[])Enum.GetValues(typeof(ViewMode)); // Step from the PENDING mode when a fade-wrapped switch is in flight: _viewMode only // updates after the ~90ms fade-out, so wheel notches faster than that would otherwise // recompute from the stale mode and retarget the same switch - several notches // collapsing into one step (2-4 clicks per mode, as first built). int idx = Array.IndexOf(modes, _pendingViewMode ?? _viewMode); int next = (idx + (forward ? 1 : -1) + modes.Length) % modes.Length; SetViewMode(modes[next]); 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. // Radios and accent dots reflect live state before the card shows - the same single // sync the Settings panel runs on open. SyncPickerState(); // SplitHost is the full document region between the rail and the window edge. Its // rail-adjacent bottom corner is left when the sidebar is left and right when the // sidebar is right. Using one viewer pane here stranded the flyouts on the left after // the sidebar moved right, and could place them in the middle of a split document. if (SplitHost is FrameworkElement pane) FlyoutPlacement.UsePane(pane, _sidebarRight); FlyoutPlacement.Attach(menu, this); menu.IsOpen = true; // 150ms ease-out, the family fade (the flyout template replaces the implicit // ContextMenu template, whose Loaded trigger normally drives this). menu.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, new Duration(TimeSpan.FromMilliseconds(150))) { EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } }); } } }