Resizable, movable minimap with quality scaling#106
Merged
sjvrensburg merged 4 commits intomainfrom Apr 14, 2026
Merged
Conversation
The minimap was a fixed 180x240 docked bottom-right. Now: - Drag the top-edge grip to move it anywhere on the window - Drag the inner-corner handle (opposite the screen edge it's docked against) to resize. Aspect-locked to the page so the thumbnail never distorts. - Position and size persist via AppConfig (MinimapWidth, Height, MarginRight, MarginBottom). - Click/drag inside as before to navigate the primary view. Tier-1 quality: when the displayed thumbnail size exceeds the cached thumbnail bitmap by >10%, render from the primary's high-DPI SKImage (`CachedImage`) instead of the small thumbnail. No extra rendering work — reuses what the primary already has. Mitchell sampling at rest, Linear during drag. Implementation: - TabViewModel.MinimapImage wraps the thumbnail SKBitmap as SKImage (cached, disposed with the tab), so the canvas can use sampling- aware DrawImage uniformly across both source paths. - Drag-distance threshold (4px) preserves "click to navigate" UX — small movements still navigate; only intentional drags move/resize. - Resize handle position auto-flips to whichever corner points into the screen (handles non-default minimap docking). - Hover/drag chrome (move stripe, resize hash) only renders when needed; otherwise the minimap looks the same as before. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three issues: 1. Drag/resize deltas were computed from e.GetPosition(this), relative to the moving control. As the control's origin moved during drag, subsequent pointer events saw a shifted relative position, feeding back into the next delta calculation — visible as resistance and jitter. Switched to window-coords for delta math; local coords still used for hit-zone detection and navigation. 2. Aspect-locked resize switched between width-driven and height-driven each frame based on which axis had the larger proportional drag. Mouse wobble flipped the dominant axis, jumping the size. Replaced with diagonal projection: project the drag vector onto the corner's aspect-ratio direction, derive both width and height from a single scalar. Continuous and aspect-preserving by construction. 3. Large minimap stuttered during rail scrolling because each redraw resampled the multi-megapixel CachedImage from scratch on the UI thread. Upload the source as a mipmapped GPU texture once (cached on the control, disposed on source change or detach) so subsequent draws are constant-cost regardless of downscale ratio. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Separate ThreadStatic SKPaint for resize-handle hash lines instead of mutating the shared grip-dot paint's Style back and forth (fragile with shared state). - Name PrimarySourceThreshold (1.1) and MaxViewportFraction (0.8). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The minimap was a fixed 180×240 thumbnail in the bottom-right corner. This PR makes it a freely draggable, resizable, quality-scaling context view:
AppConfigacross sessionsQuality (tier 1)
When the displayed thumbnail exceeds the cached thumbnail's resolution by more than 10%, the minimap transparently switches its source to the primary view's high-DPI
CachedImage. The source is uploaded to the GPU as a mipmapped texture, cached per control, so enlarged minimaps stay crisp without re-sampling a multi-megapixel bitmap on every frame.Smoothness fixes landed during implementation
s_hashLinededicated ThreadStatic paint instead of mutating a shared paint's Style mid-render.Bumps to 3.10.0.0 (minor — user-visible enhancement, no API changes).
Test plan
dotnet build RailReader2.slnx -c ReleasecleanCtrl+Mtoggle still works, click-to-navigate unchanged🤖 Generated with Claude Code