feat(ui): gear to the rail, EDIT on the panel edge, preview undo/redo
Three creator directives (2026-11-01), done ahead of the surge. **Gear moved to the preview rail** — first tab, orange (Brush.Attention), gear icon only. Left-click opens the same app menu the top bar gear owned (Settings / Bug / Feature / About), right-click goes straight to App Settings; both behaviours moved with it, so nothing lost an entry point. TopBar keeps the wordmark and EDIT. **EDIT right edge aligned to the layers panel.** The work area is margin 8 + a 360px column, the top bar is padding 16, so the shared edge is 352 inside the bar. TopBar column 0 is now a fixed 352 grid with EDIT right-aligned in it — alignment by arithmetic instead of by eye. **Preview undo/redo: Ctrl+Z / Ctrl+Shift+Z.** Models/PreviewHistory snapshots element REFERENCES plus geometry, visibility, order and membership — a deep clone would mirror every property of a dozen element subclasses and quietly miss the next one added. One snapshot per gesture (pushed at drag/resize start, never in the move loop), bounded at 50, redo survives multiple undos, cleared on layout load. Gated on EditingActive: undo while the preview shows program output would rewrite a scene nobody is looking at. Honest scope, in the type's doc comment: layout edits only. Colour/opacity/border/font edits are not captured yet, so Ctrl+Z after one of those restores the last layout change. 6 tests in PreviewHistoryTests; 19 pass across it and the neighbouring UI classes; Debug and Release both build at 0 warnings.
This commit is contained in:
@@ -459,6 +459,7 @@ public partial class PreviewPane : UserControl
|
||||
|
||||
if (selected is { } sel && IsDraggableElement(sel) && HitHandle(e.GetPosition(grid), sel))
|
||||
{
|
||||
vm.PushPreviewUndo();
|
||||
_isResizing = true;
|
||||
_resizeAspect = sel.ClipShape == ClipShape.Round ? 1 : sel.Width / Math.Max(1, sel.Height);
|
||||
grid.CaptureMouse();
|
||||
@@ -470,6 +471,9 @@ public partial class PreviewPane : UserControl
|
||||
if (hit != null)
|
||||
{
|
||||
vm.SelectedElement = hit;
|
||||
// One snapshot per gesture: pushing inside the move loop would fill the stack with
|
||||
// a hundred one-pixel steps.
|
||||
vm.PushPreviewUndo();
|
||||
_isDraggingOverlay = true;
|
||||
_grabOffset = new Point(p.X - hit.X, p.Y - hit.Y);
|
||||
grid.CaptureMouse();
|
||||
@@ -689,4 +693,23 @@ public partial class PreviewPane : UserControl
|
||||
ZoomVisualBrush.Visual = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Rail settings tab (creator ruling 2026-11-01): left-click opens the same app menu
|
||||
/// the top-bar gear used to own, so Bug / Feature / About keep their only entry point.</summary>
|
||||
private void SettingsTab_Click(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not FrameworkElement { ContextMenu: { } menu } anchor) return;
|
||||
menu.PlacementTarget = anchor;
|
||||
menu.Placement = System.Windows.Controls.Primitives.PlacementMode.Left;
|
||||
menu.IsOpen = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>Right-click goes straight to App Settings — the same shortcut the old gear had.</summary>
|
||||
private void SettingsTab_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
if (DataContext is MainViewModel { OpenSettingsCommand: { } open } && open.CanExecute(null))
|
||||
open.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user