Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 25 additions & 1 deletion Free Ruler/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {

let crosshair = NSCursor.crosshair

@IBOutlet weak var pixelsMenuItem: NSMenuItem!
@IBOutlet weak var millimetresMenuItem: NSMenuItem!
@IBOutlet weak var inchesMenuItem: NSMenuItem!

@IBOutlet weak var floatRulersMenuItem: NSMenuItem!
@IBOutlet weak var groupRulersMenuItem: NSMenuItem!
@IBOutlet weak var rulerShadowMenuItem: NSMenuItem!
Expand All @@ -41,6 +45,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {

func subscribeToPrefs() {
observers = [
prefs.observe(\Prefs.unit, options: .new) { prefs, changed in
self.updateUnitMenu()
},
prefs.observe(\Prefs.floatRulers, options: .new) { prefs, changed in
self.updateFloatRulersMenuItem()
},
Expand All @@ -54,10 +61,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func updateDisplay() {
updateUnitMenu()
updateFloatRulersMenuItem()
updateGroupRulersMenuItem()
updateRulerShadowMenuItem()
}

func updateUnitMenu() {
pixelsMenuItem?.state = prefs.unit == .pixels ? .on : .off
millimetresMenuItem?.state = prefs.unit == .millimetres ? .on : .off
inchesMenuItem?.state = prefs.unit == .inches ? .on : .off
}

func updateFloatRulersMenuItem() {
floatRulersMenuItem?.state = prefs.floatRulers ? .on : .off
Expand Down Expand Up @@ -106,7 +120,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {

crosshair.pop()
}


@IBAction func setUnitPixels(_ sender: Any) {
prefs.unit = .pixels
}
@IBAction func setUnitMillimetres(_ sender: Any) {
prefs.unit = .millimetres
}
@IBAction func setUnitInches(_ sender: Any) {
prefs.unit = .inches
}

@IBAction func toggleFloatRulers(_ sender: Any) {
prefs.floatRulers = !prefs.floatRulers
}
Expand Down
33 changes: 31 additions & 2 deletions Free Ruler/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
Expand All @@ -17,6 +17,9 @@
<outlet property="alignRulersMenuItem" destination="iKV-uW-hwy" id="KQ1-Wu-ShT"/>
<outlet property="floatRulersMenuItem" destination="GDK-AC-uC8" id="552-nT-5F2"/>
<outlet property="groupRulersMenuItem" destination="7Ga-Fb-LLc" id="WrL-X9-6QE"/>
<outlet property="inchesMenuItem" destination="lt1-Hj-2TR" id="yV0-oN-4bC"/>
<outlet property="millimetresMenuItem" destination="B6Y-Hi-AkN" id="9Cc-tZ-RRZ"/>
<outlet property="pixelsMenuItem" destination="pYR-Ba-kKi" id="Wus-JK-rs3"/>
<outlet property="rulerShadowMenuItem" destination="a8D-hN-A59" id="FZM-pS-71y"/>
</connections>
</customObject>
Expand Down Expand Up @@ -133,6 +136,31 @@
</items>
</menu>
</menuItem>
<menuItem title="Unit" id="iDP-2z-irv">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Unit" id="z2p-dA-zcS">
<items>
<menuItem title="Pixels" state="on" id="pYR-Ba-kKi">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="setUnitPixels:" target="Voe-Tx-rLC" id="CKT-cD-v5a"/>
</connections>
</menuItem>
<menuItem title="Millimetres" id="B6Y-Hi-AkN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="setUnitMillimetres:" target="Voe-Tx-rLC" id="jOc-Pv-TID"/>
</connections>
</menuItem>
<menuItem title="Inches" id="lt1-Hj-2TR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="setUnitInches:" target="Voe-Tx-rLC" id="Apf-6P-Oz8"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Options" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Options" id="HyV-fh-RgO">
Expand Down Expand Up @@ -208,6 +236,7 @@
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="-147" y="125"/>
</menu>
<menuItem title="Group Rulers" state="on" keyEquivalent="g" id="7SO-Lm-Ylr">
<modifierMask key="keyEquivalentModifierMask"/>
Expand Down
71 changes: 57 additions & 14 deletions Free Ruler/HorizontalRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,66 @@ class HorizontalRule: RuleView {
.foregroundColor: color.numbers
]

let width = Int(dirtyRect.width)
let path = NSBezierPath()
let tickScale: CGFloat
let textScale: Int
let largeTicks: Int
let mediumTicks: Int
let smallTicks: Int
let tinyTicks: Int?

switch prefs.unit {
case .millimetres:
tickScale = screen?.dpmm.width ?? NSScreen.defaultDpmm
textScale = 1
largeTicks = 10
mediumTicks = 5
smallTicks = 1
tinyTicks = nil
case .inches:
tickScale = (screen?.dpi.width ?? NSScreen.defaultDpi) / 16
textScale = 16
largeTicks = 16
mediumTicks = 8
smallTicks = 4
tinyTicks = 1
default:
tickScale = 1
textScale = 1
largeTicks = 50
mediumTicks = 10
smallTicks = 2
tinyTicks = nil
}

// substract two so ticks don't overlap with border
// subtract from this range so width var is accurate
for i in 1...width - 2 {
if i.isMultiple(of: 50) {
path.move(to: CGPoint(x: i, y: 1))
path.line(to: CGPoint(x: i, y: 10))
for i in 1...Int((dirtyRect.width - 2) / tickScale) {
let pos = CGFloat(i) * tickScale
if i.isMultiple(of: largeTicks) {
path.move(to: CGPoint(x: pos, y: 1))
path.line(to: CGPoint(x: pos, y: 10))

let label = String(i)
let label = String(i / textScale)
label.draw(
with: CGRect(x: i - 20, y: 3, width: 40, height: 20),
with: CGRect(x: pos - 20, y: 3, width: 40, height: 20),
options: .usesLineFragmentOrigin,
attributes: attrs,
context: nil
)

}
else if i.isMultiple(of: 10) {
path.move(to: CGPoint(x: i, y: 1))
path.line(to: CGPoint(x: i, y: 8))
else if i.isMultiple(of: mediumTicks) {
path.move(to: CGPoint(x: pos, y: 1))
path.line(to: CGPoint(x: pos, y: 8))
}
else if i.isMultiple(of: smallTicks) {
path.move(to: CGPoint(x: pos, y: 1))
path.line(to: CGPoint(x: pos, y: 5))
}
else if i.isMultiple(of: 2) {
path.move(to: CGPoint(x: i, y: 1))
path.line(to: CGPoint(x: i, y: 5))
else if let tinyTicks = tinyTicks, i.isMultiple(of: tinyTicks) {
path.move(to: CGPoint(x: pos, y: 1))
path.line(to: CGPoint(x: pos, y: 3))
}
}

Expand Down Expand Up @@ -113,7 +147,16 @@ class HorizontalRule: RuleView {
NSAttributedString.Key.backgroundColor: color.fill,
]

let label = String(Int(number))
let label: String
switch prefs.unit {
case .millimetres:
label = String(format: "%.1f", number / (screen?.dpmm.width ?? NSScreen.defaultDpmm))
case .inches:
label = String(format: "%.3f", number / (screen?.dpi.width ?? NSScreen.defaultDpi))
default:
label = String(Int(number))
}

label.draw(with: CGRect(x: labelX, y: 20, width: labelWidth, height: 20), options: .usesLineFragmentOrigin, attributes: attrs, context: nil)

}
Expand Down
12 changes: 12 additions & 0 deletions Free Ruler/Prefs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import Foundation
// MARK: - global shortcut to shared prefs instance
let prefs = Prefs.shared

@objc enum Unit: Int {
case pixels
case millimetres
case inches
}

class Prefs: NSObject {

// MARK: - shared singleton instance
Expand All @@ -24,6 +30,7 @@ class Prefs: NSObject {
@objc dynamic var rulerShadow : Bool
@objc dynamic var foregroundOpacity : Int
@objc dynamic var backgroundOpacity : Int
@objc dynamic var unit : Unit

// MARK: - public save method
func save() {
Expand All @@ -40,6 +47,7 @@ class Prefs: NSObject {
"rulerShadow": false,
"foregroundOpacity": 90,
"backgroundOpacity": 50,
"unit": Unit.pixels.rawValue
]

private override init() {
Expand All @@ -50,6 +58,7 @@ class Prefs: NSObject {
rulerShadow = defaults.bool(forKey: "rulerShadow")
foregroundOpacity = defaults.integer(forKey: "foregroundOpacity")
backgroundOpacity = defaults.integer(forKey: "backgroundOpacity")
unit = Unit(rawValue: defaults.integer(forKey: "unit")) ?? .pixels

super.init()

Expand All @@ -75,6 +84,9 @@ class Prefs: NSObject {
observe(\Prefs.backgroundOpacity, options: .new) { prefs, changed in
self.defaults.set(changed.newValue, forKey: "backgroundOpacity")
},
observe(\Prefs.unit, options: .new) { prefs, changed in
self.defaults.set(prefs.unit.rawValue, forKey: "unit")
},
]
}

Expand Down
34 changes: 34 additions & 0 deletions Free Ruler/RuleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,39 @@ class RuleView: NSView {
}
}
}

var screen: NSScreen? {
guard let window = window else {
return nil
}
return NSScreen.screens.first { $0.frame.contains(window.convertToScreen(frame).origin) }
}

}

fileprivate let mmPerIn: CGFloat = 25.4

public extension NSScreen {

// This is the same as what CoreGraphics assumes if no EDID data is available from the display device
// https://developer.apple.com/documentation/coregraphics/1456599-cgdisplayscreensize
static let defaultDpi: CGFloat = 72.0
static let defaultDpmm: CGFloat = defaultDpi / mmPerIn

var dpmm: CGSize {
if let resolution = (deviceDescription[.size] as? NSValue)?.sizeValue,
let screenNumber = (deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value {
let physicalSize = CGDisplayScreenSize(screenNumber)
return CGSize(width: resolution.width / physicalSize.width,
height: resolution.height / physicalSize.height)
} else {
return CGSize(width: NSScreen.defaultDpmm, height: NSScreen.defaultDpmm)
}
}

var dpi: CGSize {
return CGSize(width: mmPerIn * dpmm.width,
height: mmPerIn * dpmm.height)
}

}
Loading