Skip to content
Closed
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
61 changes: 55 additions & 6 deletions Free Ruler/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
settingsController.close()
}
}
manager.onStateChanged = { [weak self] _ in
self?.saveRulerSetState()
}
return manager
}()

Expand Down Expand Up @@ -185,6 +188,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
configureUpdater()
#endif

restoreSavedRulers()
showRulers()
}

Expand Down Expand Up @@ -400,6 +404,55 @@ class AppDelegate: NSObject, NSApplicationDelegate {
updateMouseTickTimer()
}

func restoreSavedRulers() {
if let restoredState = prefs.loadRulerSetState() {
rulerManager.restore(
restoredState.rulers,
activeRulerID: restoredState.activeRulerID
)
return
}

if let migratedState = migratedLegacyRulerState() {
rulerManager.restore([migratedState], activeRulerID: migratedState.id)
}
}

private func saveRulerSetState() {
prefs.saveRulerSetState(
rulers: rulerManager.states,
activeRulerID: rulerManager.activeRulerID
)
}

private func migratedLegacyRulerState() -> RulerInstanceState? {
let defaults = UserDefaults.standard
let horizontalAutosaveName = "horizontal-ruler"
let verticalAutosaveName = "vertical-ruler"
let hasLegacyAutosave = defaults.object(forKey: "NSWindow Frame \(horizontalAutosaveName)") != nil
|| defaults.object(forKey: "NSWindow Frame \(verticalAutosaveName)") != nil
guard hasLegacyAutosave else { return nil }

let settings = RulerSettings(defaults: prefs)
let horizontalWindow = RulerWindow(
ruler: Ruler(.horizontal, name: horizontalAutosaveName)
)
let verticalWindow = RulerWindow(
ruler: Ruler(.vertical, name: verticalAutosaveName)
)
_ = horizontalWindow.setFrameUsingName(NSWindow.FrameAutosaveName(horizontalAutosaveName))
_ = verticalWindow.setFrameUsingName(NSWindow.FrameAutosaveName(verticalAutosaveName))

return RulerInstanceState(
settings: settings,
layout: RulerLayoutState(
horizontalFrame: horizontalWindow.frame,
verticalFrame: verticalWindow.frame,
zeroCorner: settings.zeroCorner
)
)
}

func toggleRuler(orientation: Orientation) {
if !rulerManager.hasRulers && rulers.isEmpty {
createRulersIfNeeded()
Expand Down Expand Up @@ -791,12 +844,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {

@IBAction func resetRulerPositions(_ sender: Any) {
if let controller = rulerManager.activeController {
controller.state.settings.zeroCorner = Prefs.defaultZeroCorner
controller.state.layout = RulerLayoutState.defaults(
zeroCorner: Prefs.defaultZeroCorner
)
controller.state.visibility = RulerWingVisibility()
controller.show()
controller.resetPosition()
updateDisplay()
updateMouseTickTimer()
return
Expand Down Expand Up @@ -1046,6 +1094,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {

func applicationWillTerminate(_ aNotification: Notification) {
closeRulerColorPanel()
saveRulerSetState()
prefs.save()
}

Expand Down
32 changes: 31 additions & 1 deletion Free Ruler/GroupedRulerWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,16 @@ final class GroupedRulerController: NSWindowController, NSWindowDelegate, Notifi
notifyStateChanged()
}

func resetPosition() {
state.settings.zeroCorner = Prefs.defaultZeroCorner
state.layout = RulerLayoutState.defaults(
zeroCorner: Prefs.defaultZeroCorner
)
state.visibility = RulerWingVisibility()
show()
notifyStateChanged()
}

func drawMouseTick(at mouseLoc: NSPoint) {
if groupedWindow.isRuleVisible(.horizontal) {
groupedWindow.horizontalRule.drawMouseTick(at: mouseLoc)
Expand Down Expand Up @@ -1612,6 +1622,7 @@ final class RulerManager {
private(set) var controllers: [GroupedRulerController] = []
private(set) var activeRulerID: UUID?
var onActiveControllerChanged: ((GroupedRulerController?) -> Void)?
var onStateChanged: ((RulerManager) -> Void)?

init(
initialStates: [RulerInstanceState] = [],
Expand Down Expand Up @@ -1668,7 +1679,7 @@ final class RulerManager {
return controller
}

func restore(_ states: [RulerInstanceState]) {
func restore(_ states: [RulerInstanceState], activeRulerID restoredActiveRulerID: UUID? = nil) {
for controller in controllers {
controller.hide()
}
Expand All @@ -1680,6 +1691,13 @@ final class RulerManager {
for state in states where state.hasVisibleWing {
addRuler(state: state)
}

if let restoredActiveRulerID = restoredActiveRulerID,
let restoredActiveController = controller(id: restoredActiveRulerID) {
markActive(restoredActiveController)
}

notifyStateChanged()
}

func showAll() {
Expand Down Expand Up @@ -1708,13 +1726,16 @@ final class RulerManager {
activeRulerID = controllers.last?.state.id
onActiveControllerChanged?(activeController)
}

notifyStateChanged()
}

func markActive(_ controller: GroupedRulerController) {
guard controllers.contains(where: { $0 === controller }) else { return }

activeRulerID = controller.state.id
onActiveControllerChanged?(controller)
notifyStateChanged()
}

func controller(containing window: NSWindow?) -> GroupedRulerController? {
Expand All @@ -1723,6 +1744,10 @@ final class RulerManager {
return controllers.first { $0.groupedWindow === window }
}

func controller(id: UUID) -> GroupedRulerController? {
return controllers.first { $0.state.id == id }
}

private func configure(_ controller: GroupedRulerController) {
controller.onBecameActive = { [weak self, weak controller] _ in
guard let controller = controller else { return }
Expand All @@ -1733,8 +1758,13 @@ final class RulerManager {
self?.activeRulerID == controller.state.id else { return }

self?.activeRulerID = controller.state.id
self?.notifyStateChanged()
}
}

private func notifyStateChanged() {
onStateChanged?(self)
}
}
#endif

Expand Down
36 changes: 36 additions & 0 deletions Free Ruler/Prefs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ class Prefs: NSObject {
}

extension Prefs {
static let rulerSetStateKey = "rulerSetState"

static var defaultZeroCorner: ZeroCorner {
return .topLeft
}
Expand Down Expand Up @@ -189,4 +191,38 @@ extension Prefs {
from: data
)
}

func saveRulerSetState(rulers: [RulerInstanceState], activeRulerID: UUID?) {
let visibleRulers = rulers.filter(\.hasVisibleWing)
guard !visibleRulers.isEmpty else {
clearRulerSetState()
return
}

let activeRulerIDToSave = activeRulerID.flatMap { activeRulerID in
visibleRulers.contains { $0.id == activeRulerID } ? activeRulerID : nil
}
let state = StoredRulerSetState(
rulers: visibleRulers,
activeRulerID: activeRulerIDToSave
)

guard let data = try? JSONEncoder().encode(state) else { return }

UserDefaults.standard.set(data, forKey: Self.rulerSetStateKey)
}

func loadRulerSetState() -> StoredRulerSetState? {
guard let data = UserDefaults.standard.data(forKey: Self.rulerSetStateKey),
let state = try? JSONDecoder().decode(StoredRulerSetState.self, from: data),
state.schemaVersion == StoredRulerSetState.currentSchemaVersion else {
return nil
}

return state.sanitizedForRestore()
}

func clearRulerSetState() {
UserDefaults.standard.removeObject(forKey: Self.rulerSetStateKey)
}
}
33 changes: 33 additions & 0 deletions Free Ruler/Ruler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,39 @@ struct RulerInstanceState: Identifiable, Equatable, Codable {
}
}

struct StoredRulerSetState: Equatable, Codable {
static let currentSchemaVersion = 1

var schemaVersion: Int
var rulers: [RulerInstanceState]
var activeRulerID: UUID?

init(
schemaVersion: Int = StoredRulerSetState.currentSchemaVersion,
rulers: [RulerInstanceState],
activeRulerID: UUID?
) {
self.schemaVersion = schemaVersion
self.rulers = rulers
self.activeRulerID = activeRulerID
}

func sanitizedForRestore() -> StoredRulerSetState? {
let visibleRulers = rulers.filter(\.hasVisibleWing)
guard !visibleRulers.isEmpty else { return nil }

let restoredActiveRulerID = activeRulerID.flatMap { activeRulerID in
visibleRulers.contains { $0.id == activeRulerID } ? activeRulerID : nil
}

return StoredRulerSetState(
schemaVersion: schemaVersion,
rulers: visibleRulers,
activeRulerID: restoredActiveRulerID
)
}
}

struct RulerCornerPlacement: Equatable {
let xSide: RulerHorizontalSide
let ySide: RulerVerticalSide
Expand Down
1 change: 1 addition & 0 deletions Free Ruler/UITestSupport+App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extension UITestSupport {
"rulerColor",
"unit",
"zeroCorner",
Prefs.rulerSetStateKey,
"NSWindow Frame horizontal-ruler",
"NSWindow Frame vertical-ruler",
"NSWindow Frame preferencesWindow",
Expand Down
Loading