Handle grouped zero-corner flips#204
Merged
pascalpp merged 36 commits intoJun 14, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds core logic and tests for flipping the shared “zero corner” state and coordinating grouped-ruler repositioning so the non-flipped ruler preserves its prior zero-point offset when the corner changes.
Changes:
- Added
ZeroCorner.flipped(along:)to model horizontal/vertical flips between the four zero-corner states. - Added app-level
AppDelegate.flipRulers(along:)logic to preserve grouped zero-point offsets across flips and trigger redraw onzeroCornerchanges. - Added unit tests covering zero-corner flipping and grouped vs ungrouped window-movement behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| FreeRulerTests/RulerCoreTests.swift | Adds tests for zero-corner flipping and grouped/ungrouped flip window movement. |
| Free Ruler/Ruler.swift | Adds ZeroCorner.flipped(along:) helper for axis-specific corner flips. |
| Free Ruler/AppDelegate.swift | Redraws on zeroCorner changes and adds grouped flip coordination logic. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Owner
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
This was referenced Jun 13, 2026
Merged
e1573c5 to
bf70bb4
Compare
9c71cc1 to
94fdaa2
Compare
bf70bb4 to
9ee6030
Compare
94fdaa2 to
1c2d2b6
Compare
This was referenced Jun 14, 2026
Add RulerSide.opposite and update label/handle geometry to anchor unit labels and resize handles relative to the far/opposite corner derived from ZeroCornerGeometry. HorizontalRule and VerticalRule now compute label X/Y anchors using geometry.opposite sides (and use a fixed textHeight for top labels). ResizeHandleView uses tickSide.opposite to place vertical/horizontal handles correctly. Update RulerCoreTests: adjust expected rects, add a unit-label positioning test, rename/expand resize-handle assertions to check X and Y anchoring, and wrap tests to set/restore zeroCorner preference where needed.
Add RulerCornerPlacement and a new ZeroCornerGeometry.resizeHandlePlacement(for:) helper to compute the x/y sides for resize handles based on orientation and zero corner. Refactor ResizeHandleView to use placement.xSide/placement.ySide instead of separately computing tickSide, resizeSide and their opposites, simplifying control flow and removing duplicated logic. Changes in Ruler.swift and ResizeHandleView.swift; no intended behavioral change.
Introduce UnitLabelView to centralize unit label sizing, framing and drawing. Move unit label placement logic into UnitLabelView.labelFrame and ZeroCornerGeometry.unitLabelPlacement, and wire a unitLabelView subview into RuleView with install/update methods. HorizontalRule and VerticalRule now install the subview and override visibility logic to hide the label when the mouse tick overlaps it. Update project file to include the new source and adjust/extend unit tests to match the new layout behavior and verify subview positioning and baseline consistency.
Tweak UnitLabelView insets (bottom: 10→9, left/right: 7→8) to correct unit label positioning. Update RulerCoreTests expected CGRects to match the adjusted layout. Files changed: Free Ruler/UnitLabelView.swift and FreeRulerTests/RulerCoreTests.swift.
Introduce MouseTickLabelLayout to centralize mouse-tick label placement logic for both horizontal and vertical rulers, replacing per-rule mouseNumberLabelRect implementations. Add resize-handle obscuring support via setResizeHandleObscured and per-rule updateResizeHandleVisibility overrides so the resize handle fades when a mouse tick overlaps it; wire visibility updates into layout/preference changes and mouse-tick/showMouseTick setters. Update tests to reflect the new layout API and visual-obscuring behavior and remove the older mouseTickLabelResizeHandleSpacing constant.
Introduce debug-only preview tooling to visualize mouse tick label layout and offsets. Adds MouseTickLabelLayoutPreview (NSViewRepresentable) and MouseTickLabelLayoutPreviewView to draw ruler ticks, mouse tick line/label, unit label, and resize handle; plus SwiftUI helpers MouseTickLabelOffsetPreview and MouseTickLabelCornerPreview to show combinations of zero corners and orientations. Update RuleView_Previews to group horizontal/vertical ruler previews and include the new Mouse Tick Labels preview.
Replace direct prefs.zeroCorner usages with a RuleView.zeroCorner property and propagate that value to child views (UnitLabelView, ResizeHandleView, HorizontalRule, VerticalRule). Add frame(in:bounds, zeroCorner:) overloads and update layout code to use the passed zeroCorner. Introduce RuleView.installWindowBorder() and improve window size fallbacks. Add nil-checks for NSGraphicsContext before drawing label rectangles. Refactor DEBUG preview code: replace the old MouseTickLabelLayoutPreview with a compact MouseTickRulePreview that hosts new MouseTickPreviewHorizontalRule/VerticalRule subclasses, and wire preview configuration (previewZeroCorner, mouse positions, colors). Also add a new localized string key to Localizable.xcstrings.
Delete the NSViewRepresentable RuleViewPreview and the HStack of horizontal/vertical rule previews. Replace the previous preview group with a single MouseTickLabelOffsetPreview and adjust the preview layout/name. This simplifies SwiftUI previews by removing the AppKit bridging preview helper and duplicate preview entries.
Switch help icon naming from a build-number-based token to a UTC timestamp+hash token. Update FreeRuler HTML and CSS to reference the new freeruler-help-icon-<timestamp>-<hash>.png name, rename the icon file accordingly, and remove the old freeruler.png legacy copy. Update scripts/generate-app-icon.sh to generate a timestamped help_cache_token, drop the Info.plist build_number dependency, remove legacy copy behavior, and propagate the new token into styles.css and FreeRuler.html while cleaning up stale icons.
Introduce a CSS variable for the help icon and refresh related assets. - FreeRuler.html: add :root style setting --free-ruler-help-icon, update styles.css query token, tidy metadata and reformat the keyboard shortcuts/feature sections. - styles.css: switch .title-page-header background-image to use var(--free-ruler-help-icon). - Replace old help icon with a new freeruler-help-icon PNG and remove the old file. - Update multiple AppIcon image assets (various sizes) — regenerated icons. - scripts/generate-app-icon.sh: combine perl substitutions to inject the cache token and set the --free-ruler-help-icon URL in the HTML, and remove an unused variable. These changes allow the help page to reference the generated icon dynamically and support cache-busting when regenerating icons.
Add scripts/generate-help-index.sh which runs hiutil to build English.lproj.helpindex from Free Ruler/FreeRuler.help/Contents/Resources/English.lproj (script uses strict bash flags and executes from repo root). Wire the script into package.json as the "generate:help" npm script and update the packaged help index file accordingly so the help index can be regenerated as part of build tasks.
Use a fixed help bundle identifier and stop appending the build number. Updated FreeRuler.help/Contents/Info.plist to set CFBundleIdentifier to com.pascal.freeruler.help and adjusted CFBundleVersion to 491. Replaced the templated CFBundleHelpBookName in Free Ruler/Info.github.plist and Free Ruler/Info.plist with the fixed help ID. Removed the code in scripts/release/set-version.js that modified the help CFBundleIdentifier per-build so the help bundle ID remains stable across builds.
Introduce an adjustable preview for mouse tick label offsets used in DEBUG builds. Adds a mouseTickLabelPreviewRulerLength constant and refactors MouseTickLabelOffsetPreview into smaller views: heading, grid, controls, slider panel, and individual sliders with a numeric TextField (clamped and rounded). Replaces hardcoded 260 ruler length usage, extracts a cornerPreview helper, and updates the PreviewProvider to use the interactive controls for easier testing of label offsets.
Introduce RuleView.resizeHandleEndRegionContains(...) to encapsulate logic that determines whether the mouse tick overlaps the resize-handle end region based on ruler orientation and the current zeroCorner/resize side. Replace the inline min/max checks in HorizontalRule and VerticalRule with calls to this helper. Expand and rename unit tests to cover obscuring behavior from the leading edge to the ruler end across different zeroCorner configurations. Includes an assertionFailure fallback for unexpected cases.
Prevent mouse-tick/unit labels from overlapping the ruler resize handle and from incorrectly hiding near the zero edge. MouseTickLabelLayout now accepts an optional resizeHandleFrame and tickGap was renamed to tickLabelSpacing; layout logic was extended with horizontal/vertical helper checks to prefer the side that doesn't collide with the resize handle. RuleView gained unitLabelZeroRegionContains and HorizontalRule/VerticalRule use it to compute unit-label visibility based on growth direction and bounds. Debug previews and sliders were adjusted to exercise the new behavior, and unit tests were added to cover zero-edge hiding and label flipping around the resize handle.
Pass zeroCorner into child views and clean up geometry/layout logic. ResizeHandleView now computes grip and slot frames (gripRect/gripSize/slotFrame) and draws grip/bg relative to the grip; it accepts a zeroCorner override. UnitLabelView gets explicit padding, safer draw rect to leave room for descenders, and accepts zeroCorner for placement. MouseTickLabelLayout was extended to handle unitLabelFrame, vertical lanes, and revised offsets; added labelBackgroundFrame helper. HorizontalRule/VerticalRule use growthDirection-aware mouse tick line helpers and simplified unit label / resize-handle visibility checks. Tests updated and new unit tests added to cover the new layout behavior and zeroCorner override.
69d4e36 to
4167666
Compare
9ee6030 to
03892c5
Compare
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
Validation
xcodebuild -project "Free Ruler.xcodeproj" -scheme "Free Ruler" test -only-testing:FreeRulerTestsPart of #192. Addresses #198.