Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
98db6e0
Refactor mouse tick layout & handle visibility
pascalpp Jun 13, 2026
0b11ad3
Add mouse tick label preview views
pascalpp Jun 13, 2026
c9417d9
Propagate zeroCorner and refactor previews
pascalpp Jun 13, 2026
0bf9667
Remove RuleViewPreview and simplify previews
pascalpp Jun 13, 2026
386742c
Update Free Ruler help screen
pascalpp Jun 13, 2026
6523267
Use current app icon in help book
pascalpp Jun 13, 2026
185dfef
Update help icon when generating app icons
pascalpp Jun 13, 2026
45ddf94
Stabilize Free Ruler help book caching
pascalpp Jun 14, 2026
9329e8a
Use timestamped help icon filenames
pascalpp Jun 14, 2026
86e6523
Use CSS var for help icon and refresh assets
pascalpp Jun 14, 2026
bfc0fca
Update project.pbxproj
pascalpp Jun 14, 2026
5fdb982
Update project.pbxproj
pascalpp Jun 14, 2026
f6d532c
Add help-index generator and npm script
pascalpp Jun 14, 2026
734415a
Fix help bundle identifier & version handling
pascalpp Jun 14, 2026
9ce5970
Update set-version.js
pascalpp Jun 14, 2026
7f402a8
revert help menu handler
pascalpp Jun 14, 2026
90061af
Update bump-version.js
pascalpp Jun 14, 2026
2b57337
Update generate-app-icon.sh
pascalpp Jun 14, 2026
bd96202
Merge pull request #215 from pascalpp/pascal/213-update-help
pascalpp Jun 14, 2026
d82253a
Add interactive mouse tick label preview UI
pascalpp Jun 14, 2026
2589673
Centralize resize handle end-region check
pascalpp Jun 14, 2026
6216055
Avoid mouse tick label overlapping resize handle
pascalpp Jun 14, 2026
3c6dcae
Propagate zeroCorner and refactor geometry
pascalpp Jun 14, 2026
d23d825
tweak mouse tick layout and update tests
pascalpp Jun 14, 2026
406ebc1
Address PR feedback on ruler labels and help assets
pascalpp Jun 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Free Ruler.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
50B1D3532D05D00100B1D135 /* RulerCursorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D3522D05D00100B1D135 /* RulerCursorController.swift */; };
50B1D3552D05E00000B1D138 /* RulerTickLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D3542D05E00000B1D138 /* RulerTickLayout.swift */; };
50B1D3592D06000100B1D139 /* AppIconRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D3582D06000100B1D139 /* AppIconRenderer.swift */; };
50B1D3602D06000600B1D139 /* AppIconGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D35F2D06000600B1D139 /* AppIconGenerator.swift */; };
50B1D35D2D06000400B1D139 /* AppIconRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D3582D06000100B1D139 /* AppIconRenderer.swift */; };
50B1D3602D06000600B1D139 /* AppIconGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D35F2D06000600B1D139 /* AppIconGenerator.swift */; };
50B1D3612D06000700B1D139 /* AppIconGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D35F2D06000600B1D139 /* AppIconGenerator.swift */; };
50B1D3652D06010100B1D139 /* AppStoreScreenshotPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D3642D06010100B1D139 /* AppStoreScreenshotPreview.swift */; };
50B1D3662D06010200B1D139 /* AppStoreScreenshotPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B1D3642D06010100B1D139 /* AppStoreScreenshotPreview.swift */; };
Expand Down Expand Up @@ -142,7 +142,7 @@
8F629823243003EA004F9099 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = "<group>"; };
8F629825243003F6004F9099 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PreferencesController.xib; sourceTree = "<group>"; };
8F629828243003FF004F9099 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/PreferencesController.strings; sourceTree = "<group>"; };
AB053EE93E1DC9AF341A8D4F /* Free Ruler.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = "Free Ruler.app"; path = "Free Ruler.app"; sourceTree = BUILT_PRODUCTS_DIR; };
AB053EE93E1DC9AF341A8D4F /* Free Ruler.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Free Ruler.app"; sourceTree = BUILT_PRODUCTS_DIR; };
B894A5002BBFE61A005A3B6F /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainMenu.strings; sourceTree = "<group>"; };
B894A5012BBFE61A005A3B6F /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/PreferencesController.strings; sourceTree = "<group>"; };
D9DBE8A12C791B1600A42589 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainMenu.strings; sourceTree = "<group>"; };
Expand Down
1 change: 0 additions & 1 deletion Free Ruler/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
#endif

showRulers()

}

#if DEBUG
Expand Down
6 changes: 3 additions & 3 deletions Free Ruler/FreeRuler.help/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1</string>
<string>2</string>
<key>CFBundleSignature</key>
<string>hbwr</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>2</string>
<key>HPDBookAccessPath</key>
<string>FreeRuler.html</string>
<key>HPDBookIconPath</key>
<string></string>
<key>HPDBookIndexPath</key>
<string>English.lproj.helpindex</string>
<key>HPDBookKBProduct</key>
<string>freeruler1</string>
<string>freeruler2</string>
<key>HPDBookTitle</key>
<string>Free Ruler Help</string>
<key>HPDBookType</key>
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,39 +1,107 @@
<!doctype html>
<html>

<head>
<meta charset="UTF-8">
<title>Free Ruler Help</title>
<link rel="stylesheet" href="../shrd/styles.css"/>
<meta name="keywords" content="free ruler, shortcuts, keyboard, float, group, preferences"/>
<meta name="description" content="Free Ruler Help"/>
<meta name="robots" content="anchors"/>
<meta name="robots" content="keywords"/>
<link rel="stylesheet" href="../shrd/styles.css?icon=bb4e930f489c" />
<style>
:root {
--free-ruler-help-icon: url("../shrd/freeruler-help-icon-bb4e930f489c.png");
}
</style>
<meta name="keywords"
content="free ruler, rulers, shortcuts, keyboard, float, group, shadow, origin, units, color, preferences" />
<meta name="description" content="Free Ruler Help" />
<meta name="robots" content="anchors" />
<meta name="robots" content="keywords" />
</head>

<body>

<a name="WelcomePage"></a>

<h1 class="title-page-header">
Free Ruler Help
</h1>

<h2>
Keyboard Shortcuts
</h2>

<table class="keyboard-shortcuts">
<tbody>
<tr><th></th><th>F</th><td>Float/unfloat rulers above other windows</td></tr>
<tr><th></th><th>G</th><td>Group/ungroup rulers</td></tr>
<tr><th></th><th>S</th><td>Show/hide ruler shadows</td></tr>
<tr><th></th><th>O</th><td>Orient rulers at mouse location</td></tr>
<tr><th></th><th>U</th><td>Cycle units: pixels, millimeters, and inches</td></tr>
<tr><th>⇧</th><th>H</th><td>Flip the horizontal ruler origin</td></tr>
<tr><th>⇧</th><th>V</th><td>Flip the vertical ruler origin</td></tr>
<tr><th>⌘</th><th>R</th><td>Reset ruler positions to default</td></tr>
<tr><th>⌘</th><th>,</th><td>Open Preferences</td></tr>
</tbody>
</table>
<a name="WelcomePage"></a>

<h1 class="title-page-header">
Free Ruler Help
</h1>

<h2>
Keyboard Shortcuts
</h2>

<table class="keyboard-shortcuts">
<tbody>
<tr>
<th></th>
<th>H</th>
<td>Hide or show the horizontal ruler</td>
</tr>
<tr>
<th></th>
<th>V</th>
<td>Hide or show the vertical ruler</td>
</tr>
<tr>
<th>⇧</th>
<th>H</th>
<td>Flip the horizontal ruler origin</td>
</tr>
<tr>
<th>⇧</th>
<th>V</th>
<td>Flip the vertical ruler origin</td>
</tr>
<tr>
<th></th>
<th>F</th>
<td>Float/unfloat rulers above other windows</td>
</tr>
<tr>
<th></th>
<th>G</th>
<td>Group/ungroup rulers</td>
</tr>
<tr>
<th></th>
<th>S</th>
<td>Show/hide ruler shadows</td>
</tr>
<tr>
<th></th>
<th>O</th>
<td>Orient rulers at mouse location</td>
</tr>
<tr>
<th></th>
<th>U</th>
<td>Cycle units: pixels, millimeters, and inches</td>
</tr>
<tr>
<th>⌘</th>
<th>R</th>
<td>Reset ruler positions to default</td>
</tr>
<tr>
<th>⌘</th>
<th>,</th>
<td>Open Preferences</td>
</tr>
</tbody>
</table>

<h2>
Features
</h2>

<ul class="features">
<li>Show horizontal and vertical rulers in pixels, millimeters, or inches.</li>
<li>Resize rulers, move them independently, or keep them grouped together.</li>
<li>Customize the ruler color in Preferences.</li>
<li>Float rulers above other windows and show or hide ruler shadows.</li>
<li>Align rulers at the mouse location, reset them to default positions, or flip their origins.</li>
<li>Show, hide, and reopen rulers from the menu or keyboard.</li>
</ul>

</body>

</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
15 changes: 14 additions & 1 deletion Free Ruler/FreeRuler.help/Contents/Resources/shrd/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", Arial, sans-serif;
}

h2 {
margin-top: 40px;
}

.title-page-header {
background-image: url(./freeruler.png);
background-image: var(--free-ruler-help-icon);
background-size: 200px 200px;
background-repeat: no-repeat;
padding-top: 220px;
Expand All @@ -20,3 +24,12 @@ body {
text-align: left;
padding-left: 10px;
}

.features {
margin-top: 0;
padding-left: 22px;
}

.features li {
margin-bottom: 8px;
}
86 changes: 45 additions & 41 deletions Free Ruler/HorizontalRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class HorizontalRule: RuleView {
var mouseTickX: CGFloat = 0 {
didSet {
if mouseTickX != oldValue {
updateResizeHandleVisibility()
updateUnitLabelVisibility()
needsDisplay = true
}
Expand All @@ -38,7 +39,7 @@ class HorizontalRule: RuleView {
let height = dirtyRect.height
let path = NSBezierPath()
let tickLayout = RulerTickLayout(unit: unit, screen: screen)
let geometry = ZeroCornerGeometry(zeroCorner: prefs.zeroCorner)
let geometry = ZeroCornerGeometry(zeroCorner: zeroCorner)
let tickSide = geometry.tickSide(for: .horizontal)
let growthDirection = geometry.growthDirection(for: .horizontal)

Expand Down Expand Up @@ -116,9 +117,11 @@ class HorizontalRule: RuleView {
func drawMouseTick(_ mouseTickX: CGFloat) {
let mouseTick = NSBezierPath()
let height: CGFloat = 40
let growthDirection = ZeroCornerGeometry(zeroCorner: zeroCorner).growthDirection(for: .horizontal)
let lineX = mouseTickLineX(forTickX: mouseTickX, growthDirection: growthDirection)

mouseTick.move(to: CGPoint(x: mouseTickX, y: 0))
mouseTick.line(to: CGPoint(x: mouseTickX, y: height))
mouseTick.move(to: CGPoint(x: lineX, y: 0))
mouseTick.line(to: CGPoint(x: lineX, y: height))

mouseTick.transform(using: transformer)

Expand All @@ -138,57 +141,34 @@ class HorizontalRule: RuleView {
let labelSize = label.size()

let labelRect = mouseNumberLabelRect(
number: mouseTickX,
tickX: mouseTickX,
labelSize: labelSize,
rulerSize: CGSize(width: width, height: height)
)

guard NSGraphicsContext.current != nil else { return }
label.draw(
with: labelRect,
context: nil
)
}

func mouseNumberLabelRect(number: CGFloat, labelSize: CGSize, rulerSize: CGSize) -> CGRect {
let labelOffset: CGFloat = 5
let tickSide = ZeroCornerGeometry(zeroCorner: prefs.zeroCorner).tickSide(for: .horizontal)

let rightPosition = number + labelOffset
let leftPosition = number - labelOffset - labelSize.width
var minLabelLeft = labelOffset
var maxLabelRight = rulerSize.width - labelOffset

if let resizeHandleExclusionFrame = resizeHandleExclusionFrame {
if resizeHandleExclusionFrame.midX < rulerSize.width / 2 {
minLabelLeft = max(
minLabelLeft,
resizeHandleExclusionFrame.maxX + mouseTickLabelResizeHandleSpacing
)
} else {
maxLabelRight = min(
maxLabelRight,
resizeHandleExclusionFrame.minX - mouseTickLabelResizeHandleSpacing
)
}
}

let pinnedRightPosition = maxLabelRight - labelSize.width
let rightLabelX = max(min(rightPosition, pinnedRightPosition), minLabelLeft)
let leftLabelX = max(min(leftPosition, pinnedRightPosition), minLabelLeft)
let labelX = number < rightLabelX ? rightLabelX : leftLabelX

return CGRect(
x: labelX,
y: tickSide == .bottom ? rulerSize.height - labelSize.height : 0,
width: labelSize.width,
height: labelSize.height
func mouseNumberLabelRect(tickX: CGFloat, labelSize: CGSize, rulerSize: CGSize) -> CGRect {
return MouseTickLabelLayout.labelFrame(
labelSize: labelSize,
rulerSize: rulerSize,
orientation: .horizontal,
zeroCorner: zeroCorner,
tickPosition: tickX,
resizeHandleFrame: resizeHandleExclusionFrame,
unitLabelFrame: unitLabelFrame
)
}

override func updateUnitLabelVisibility() {
guard showMouseTick,
mouseTickX > 0,
mouseTickX < windowWidth,
mouseTickX >= bounds.minX,
mouseTickX <= bounds.maxX,
let frame = unitLabelFrame else {
setUnitLabelHidden(false)
return
Expand All @@ -197,6 +177,18 @@ class HorizontalRule: RuleView {
setUnitLabelHidden(frame.minX <= mouseTickX && mouseTickX <= frame.maxX)
}

override func updateResizeHandleVisibility() {
guard showMouseTick,
mouseTickX >= bounds.minX,
mouseTickX <= bounds.maxX,
let frame = resizeHandleExclusionFrame else {
setResizeHandleObscured(false)
return
}

setResizeHandleObscured(frame.minX <= mouseTickX && mouseTickX <= frame.maxX)
}

func tickX(
forOffset offset: CGFloat,
rulerWidth: CGFloat,
Expand Down Expand Up @@ -252,7 +244,7 @@ class HorizontalRule: RuleView {
}

func mouseNumber(forTickX mouseTickX: CGFloat, rulerWidth: CGFloat) -> CGFloat {
let growthDirection = ZeroCornerGeometry(zeroCorner: prefs.zeroCorner).growthDirection(for: .horizontal)
let growthDirection = ZeroCornerGeometry(zeroCorner: zeroCorner).growthDirection(for: .horizontal)

switch growthDirection {
case .positive:
Expand All @@ -262,12 +254,24 @@ class HorizontalRule: RuleView {
}
}

func mouseTickLineX(
forTickX mouseTickX: CGFloat,
growthDirection: RulerGrowthDirection
) -> CGFloat {
switch growthDirection {
case .positive:
return mouseTickX
case .negative:
return mouseTickX - 1
}
}

func unitLabelRect(labelSize: NSSize, rulerSize: NSSize) -> CGRect {
return UnitLabelView.labelFrame(
labelSize: labelSize,
rulerSize: rulerSize,
orientation: .horizontal,
zeroCorner: prefs.zeroCorner
zeroCorner: zeroCorner
)
}

Expand Down
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_128x128@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_16x16@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_256x256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_256x256@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_32x32@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Free Ruler/Images.xcassets/AppIcon.appiconset/icon_512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 11 additions & 1 deletion Free Ruler/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,16 @@
}
}
},
"Mouse Tick Label Offsets (mouseX: %@, mouseY: %@)" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Mouse Tick Label Offsets (mouseX: %1$@, mouseY: %2$@)"
}
}
}
},
"Reset ruler color" : {
"comment" : "Tooltip and accessibility label for the button that restores the default ruler color",
"extractionState" : "manual",
Expand Down Expand Up @@ -1053,4 +1063,4 @@
}
},
"version" : "1.1"
}
}
Loading