Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
edeeebd
Placeholder programmatic settings window
rxhanson Sep 17, 2026
15a4125
Initial outline view for keyboard shortcuts settings
rxhanson Sep 17, 2026
0e47fcc
Align cell view contents like storyboard
rxhanson Sep 17, 2026
75ed15b
Sort of similar table view experience to storyaboard
rxhanson Sep 17, 2026
410d222
Minor adjustments for the outline view
rxhanson Sep 17, 2026
f42d926
Size and position adjusments
rxhanson Sep 18, 2026
a5048ff
Save/restore size of each settings tab
rxhanson Sep 18, 2026
9c14706
Moved extra settings popover into its own file
rxhanson Sep 18, 2026
13a5a9c
An initial take on the SwiftUI general settings
rxhanson Sep 21, 2026
755845e
More adjustments on the SwiftUI general settings
rxhanson Sep 21, 2026
f7a6777
Separate section for updates
rxhanson Sep 21, 2026
c388729
More UI tweaks
rxhanson Sep 21, 2026
b34e2f3
Todo Mode spacing
rxhanson Sep 21, 2026
593bb16
Frame size
rxhanson Sep 21, 2026
aa13ebb
Shortcuts frame size
rxhanson Sep 21, 2026
78e9139
Todo app width reflecting percentage/pixel change
rxhanson Sep 21, 2026
38946b4
Separate App Settings pane
rxhanson Sep 21, 2026
7f0c795
App Settings icon
rxhanson Sep 21, 2026
efc6d3b
Reasonable window sizes for panes
rxhanson Sep 21, 2026
aafe253
Reasonable window sizes for panes
rxhanson Sep 21, 2026
2e665dc
SwiftUI SnapAreaSettingsView
rxhanson Sep 21, 2026
7992441
Hide blur appearance if not applicable
rxhanson Sep 21, 2026
eb1037b
Minor visual updates
rxhanson Sep 21, 2026
bd90185
Only show landscape text if portrait is displayed
rxhanson Sep 21, 2026
7667653
Add in images to menu
rxhanson Sep 21, 2026
96c154b
No longer popovers for grid views
rxhanson Sep 21, 2026
87698e5
Mac desktop graphic for snap area config
rxhanson Sep 21, 2026
6693a0a
More sections in behavior settings
rxhanson Sep 21, 2026
62c8b8e
Clicking on more row will now expand
rxhanson Sep 21, 2026
b167dba
Better outline view handling of groups
rxhanson Sep 22, 2026
6d494dc
Added extras category
rxhanson Sep 22, 2026
ac80b6f
ellpsis instead of More
rxhanson Sep 22, 2026
3734187
Filling out extras section
rxhanson Sep 22, 2026
dc5312e
updating icons & titles from Extras
rxhanson Sep 22, 2026
280f727
Exclude larger/smaller width from menu
rxhanson Sep 22, 2026
605a45c
Rough first cut at converting non-shortcut Extras to SwiftUI
rxhanson Sep 22, 2026
79af761
Merged extra settings into behavior settings
rxhanson Sep 22, 2026
6498593
Moving configure buttons into place for extra settings
rxhanson Sep 22, 2026
3d5b044
Add settings popover for specific window action shortcuts
rxhanson Sep 23, 2026
58fdf10
Minor code clean up
rxhanson Sep 23, 2026
6b8ae80
Only expand/collapse when title is clicked, not entire header
rxhanson Sep 23, 2026
7566696
Better initial window sizing
rxhanson Sep 23, 2026
b30edd4
Updated Maximize Settings
rxhanson Sep 23, 2026
c40c487
Some cleanup
rxhanson Sep 23, 2026
f2e0bbc
Move todo up one
rxhanson Sep 23, 2026
3ab1ca1
Move combined display mode setting
rxhanson Sep 23, 2026
1b2f5f1
Minor settings adjustments
rxhanson Sep 23, 2026
4fa6eb0
Fixing split ratio display
rxhanson Sep 23, 2026
0db53ef
Put Sections into disclosure groups
rxhanson Sep 23, 2026
7a39cd8
symbols on disclosure groups, and cleaned up spacinog
rxhanson Sep 23, 2026
f133039
Move Animate windows setting into Behavior
rxhanson Sep 23, 2026
9b0fe1e
Fix show stacked window list on hover not reflecting keyboard shortcu…
rxhanson Sep 23, 2026
19d3541
Wording
rxhanson Sep 23, 2026
62bbdfb
shortcut recorder height
rxhanson Sep 23, 2026
eaf1762
Post additional sizes in menu setting change
rxhanson Sep 23, 2026
2b586de
Moving things around
rxhanson Sep 23, 2026
ac8e746
Added Localizable.xcstrings
rxhanson Sep 23, 2026
6d57b81
SwiftUI WelcomeView
rxhanson Sep 23, 2026
84f06b4
Moved todo about into swiftui popover
rxhanson Sep 23, 2026
e6359e5
Accessibility Auth UI in SwiftUI
rxhanson Sep 24, 2026
8f93eb5
Better visuals for snap area grid
rxhanson Sep 24, 2026
e609173
Fix some of the display names
rxhanson Sep 24, 2026
624ba46
Refine comments
rxhanson Sep 24, 2026
84ef9f5
capitalization
rxhanson Sep 24, 2026
79a0214
Starting path toward localization migration
rxhanson Sep 24, 2026
7bd7612
Ordering settings
rxhanson Sep 24, 2026
5bdae64
Migrated main.xcstrings into localizable.xcstrings
rxhanson Sep 24, 2026
d606731
More localization
rxhanson Sep 24, 2026
5702ede
Removed old storyboard items and corresponding appkit files
rxhanson Sep 24, 2026
af5147a
Pulled out some unused strings
rxhanson Sep 24, 2026
ccea6d2
Make Smaller & Make larger localizations
rxhanson Sep 24, 2026
2be6c35
Remove unused strings
rxhanson Sep 24, 2026
b80cae9
Remove vertical thirds from menu
rxhanson Sep 24, 2026
4e254a4
Remove unnecessary files
rxhanson Sep 24, 2026
2e541c8
Remove unnecessary files
rxhanson Sep 24, 2026
60c1278
Remove localization testing scheme
rxhanson Sep 24, 2026
a439302
Minor localization and app delegate changes
rxhanson Sep 24, 2026
10ab817
Fix over eager 8th replacements
rxhanson Sep 24, 2026
086cdeb
Remove stale xcstrings
rxhanson Sep 24, 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
106 changes: 70 additions & 36 deletions Rectangle.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
LastUpgradeVersion = "2700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,94 @@
/// AccessibilityAuthorization.swift

import Foundation
import Cocoa
import SwiftUI

class AccessibilityAuthorization {
class AccessibilityAuthorization: ObservableObject {

private var accessibilityWindowController: NSWindowController?
private var windowController: NSWindowController?
private var timer: Timer?

@discardableResult
public func checkAccessibility(completion: @escaping () -> Void) -> Bool {
if !AXIsProcessTrusted() {

accessibilityWindowController = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "AccessibilityWindowController") as? NSWindowController

NSApp.activate(ignoringOtherApps: true)
accessibilityWindowController?.showWindow(self)
pollAccessibility(completion: completion)
showWindow(completion: completion)
startPolling(completion: completion)
return false
} else {
return true
}
}

private func pollAccessibility(completion: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
private func showWindow(completion: @escaping () -> Void) {
if windowController == nil {
let rootView = AccessibilityView(manager: self)
let hostingController = NSHostingController(rootView: rootView)

let window = CustomAccessibilityWindow(
contentViewController: hostingController
)

let wc = NSWindowController(window: window)
self.windowController = wc
}

NSApp.activate(ignoringOtherApps: true)
DispatchQueue.main.async {
self.windowController?.window?.center()
self.windowController?.showWindow(self)
}
}

private func startPolling(completion: @escaping () -> Void) {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { [weak self] t in
guard let self = self else { return }

if AXIsProcessTrusted() {
self.accessibilityWindowController?.close()
self.accessibilityWindowController = nil
t.invalidate()
self.timer = nil
self.closeWindow()
completion()
} else {
self.pollAccessibility(completion: completion)
}
}
}

func showAuthorizationWindow() {
if accessibilityWindowController?.window?.isMiniaturized == true {
accessibilityWindowController?.window?.deminiaturize(self)
if windowController?.window?.isMiniaturized == true {
windowController?.window?.deminiaturize(self)
}
NSApp.activate(ignoringOtherApps: true)
}

private func closeWindow() {
windowController?.close()
windowController = nil
}
}

// MARK: - Window Handling Termination on Close

private class CustomAccessibilityWindow: NSWindow {

init(contentViewController: NSViewController) {
super.init(
contentRect: NSRect(x: 0, y: 0, width: 320, height: 380),
styleMask: [.titled, .closable, .fullSizeContentView],
backing: .buffered,
defer: false
)

self.contentViewController = contentViewController
self.titlebarAppearsTransparent = true
self.titleVisibility = .hidden
self.isMovableByWindowBackground = true

let closeButton = self.standardWindowButton(.closeButton)
closeButton?.target = self
closeButton?.action = #selector(closeButtonClicked)
}

@objc private func closeButtonClicked() {
exit(1)
}
}
70 changes: 70 additions & 0 deletions Rectangle/AccessibilityAuthorization/AccessibilityView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/// AccessibilityView.swift

import SwiftUI
import AppKit

// MARK: - SwiftUI Views

struct AccessibilityView: View {
@ObservedObject var manager: AccessibilityAuthorization

var body: some View {
VStack(spacing: 16) {
Text("Authorize Rectangle")
.font(.system(size: 20, weight: .semibold))

if let appIcon = NSApp.applicationIconImage {
Image(nsImage: appIcon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 64, height: 64)
.cornerRadius(12)
}

Text("Rectangle needs your permission to control your window positions.")
.font(.system(size: 13, weight: .regular))
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.center)

Text(systemSettingsPath)
.font(.system(size: 11, weight: .medium))
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: 250)

Button(action: openSystemSettings) {
Text("Open System Settings")
.font(.system(size: 13, weight: .medium))
.frame(maxWidth: .infinity)
.padding(.vertical, 4)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.padding(.horizontal, 20)

Text("Enable Rectangle.app")
.font(.system(size: 13, weight: .medium))
.foregroundColor(.secondary)
.padding(.bottom, 8)
}
.padding(24)
.frame(width: 320)
}

private var systemSettingsPath: String {
if #available(macOS 27, *) {
return String(localized: "Go to System Settings → Privacy & Security → Device Control and Data Access")
} else {
return String(localized: "Go to System Settings → Privacy & Security → Accessibility")
}
}

private func openSystemSettings() {
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
NSWorkspace.shared.open(url)
}
}
}

This file was deleted.

24 changes: 12 additions & 12 deletions Rectangle/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private var titleBarManager: TitleBarManager!
private var greenButtonManager: GreenButtonManager!

private var prefsWindowController: NSWindowController?
private var settingsWindowController: NSWindowController?

private var prevActiveAppObservation: NSKeyValueObservation?
private var prevActiveApp: NSRunningApplication?
Expand Down Expand Up @@ -56,7 +56,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
checkLaunchOnLogin()

let alreadyTrusted = accessibilityAuthorization.checkAccessibility {
self.showWelcomeWindow()
WelcomeView.show()
self.checkForConflictingApps()
self.openPreferences(self)
self.statusItem.statusMenu = self.mainStatusMenu
Expand Down Expand Up @@ -254,11 +254,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

@IBAction func openPreferences(_ sender: Any) {
if prefsWindowController == nil {
prefsWindowController = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "PrefsWindowController") as? NSWindowController
if settingsWindowController == nil {
settingsWindowController = SettingsWindowController()
}
NSApp.activate(ignoringOtherApps: true)
prefsWindowController?.showWindow(self)
settingsWindowController?.showWindow(self)
}

@IBAction func showAbout(_ sender: Any) {
Expand Down Expand Up @@ -304,7 +304,7 @@ extension AppDelegate: NSMenuDelegate {
}

if let frontAppName = ApplicationToggle.frontAppName {
let ignoreString = NSLocalizedString("D99-0O-MB6.title", tableName: "Main", value: "Ignore frontmost.app", comment: "")
let ignoreString = String(localized: "Ignore frontmost.app")
ignoreMenuItem.title = ignoreString.replacingOccurrences(of: "frontmost.app", with: frontAppName)
ignoreMenuItem.state = ApplicationToggle.shortcutsDisabled ? .on : .off
ignoreMenuItem.isHidden = false
Expand Down Expand Up @@ -374,7 +374,7 @@ extension AppDelegate: NSMenuDelegate {
var menuIndex = 0
var categoryMenus: [CategoryMenu] = []
for action in WindowAction.active {
guard let displayName = action.displayName else { continue }
guard !action.excludedFromMenu, let displayName = action.displayName else { continue }
let newMenuItem = NSMenuItem(title: displayName, action: #selector(executeMenuWindowAction), keyEquivalent: "")
newMenuItem.representedObject = action
if #available(macOS 27.0, *) {
Expand Down Expand Up @@ -484,26 +484,26 @@ extension AppDelegate {
private func addTodoModeMenuItems(startingIndex: Int) {
var menuIndex = startingIndex

let todoModeItemTitle = NSLocalizedString("Enable Todo Mode", tableName: "Main", value: "", comment: "")
let todoModeItemTitle = String(localized: "Enable Todo Mode")
let todoModeMenuItem = NSMenuItem(title: todoModeItemTitle, action: #selector(toggleTodoMode), keyEquivalent: "")
todoModeMenuItem.tag = TodoItem.mode.tag
todoModeMenuItem.target = self
mainStatusMenu.insertItem(todoModeMenuItem, at: menuIndex)
menuIndex += 1

let todoAppItemTitle = NSLocalizedString("Use frontmost.app as Todo App", tableName: "Main", value: "", comment: "")
let todoAppItemTitle = String(localized: "Use frontmost.app as Todo App")
let todoAppMenuItem = NSMenuItem(title: todoAppItemTitle, action: #selector(setTodoApp), keyEquivalent: "")
todoAppMenuItem.tag = TodoItem.app.tag
mainStatusMenu.insertItem(todoAppMenuItem, at: menuIndex)
menuIndex += 1

let todoWindowItemTitle = NSLocalizedString("Use as Todo Window", tableName: "Main", value: "", comment: "")
let todoWindowItemTitle = String(localized: "Use as Todo Window")
let todoWindowMenuItem = NSMenuItem(title: todoWindowItemTitle, action: #selector(setTodoWindow), keyEquivalent: "")
todoWindowMenuItem.tag = TodoItem.window.tag
mainStatusMenu.insertItem(todoWindowMenuItem, at: menuIndex)
menuIndex += 1

let todoReflowItemTitle = NSLocalizedString("Reflow Todo", tableName: "Main", value: "", comment: "")
let todoReflowItemTitle = String(localized: "Reflow Todo")
let todoReflowItem = NSMenuItem(title: todoReflowItemTitle, action: #selector(todoReflow), keyEquivalent: "")
todoReflowItem.tag = TodoItem.reflow.tag
mainStatusMenu.insertItem(todoReflowItem, at: menuIndex)
Expand Down Expand Up @@ -554,7 +554,7 @@ extension AppDelegate {
}

if let frontAppName = ApplicationToggle.frontAppName {
let appString = NSLocalizedString("Use frontmost.app as Todo App", tableName: "Main", value: "", comment: "")
let appString = String(localized: "Use frontmost.app as Todo App")
todoAppMenuItem.title = appString.replacingOccurrences(
of: "frontmost.app", with: frontAppName)
todoAppMenuItem.isEnabled = !applicationToggle.todoAppIsActive()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"images" : [
{
"idiom" : "universal",
"filename" : "tileColumnsTemplate.png",
"scale" : "1x"
},
{
"filename" : "screenIcons-tileRowsCols.png",
"idiom" : "universal",
"scale" : "2x"
},
Expand All @@ -15,7 +15,7 @@
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
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.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"images" : [
{
"idiom" : "universal",
"filename" : "tileRowsTemplate.png",
"scale" : "1x"
},
{
"filename" : "tileRows.png",
"idiom" : "universal",
"scale" : "2x"
},
Expand All @@ -15,7 +15,7 @@
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"filename" : "appSettingsTemplate.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "appSettingsTemplate_2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
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.
Loading
Loading