Skip to content
Open
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
29 changes: 7 additions & 22 deletions Amethyst.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
1A4B46EB20AA7717003D5110 /* NSTableView+Amethyst.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4B46EA20AA7717003D5110 /* NSTableView+Amethyst.swift */; };
1AA5BA9C2D8C53E6004D6976 /* FloatingGridLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AA5BA9B2D8C53E6004D6976 /* FloatingGridLayout.swift */; };
2A6D9A4125E5D24D006A36B5 /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6D9A4025E5D24D006A36B5 /* AppManager.swift */; };
4000DB10239CA07000365A0C /* WideLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4000DB0F239CA07000365A0C /* WideLayoutTests.swift */; };
400540F22325CDF4004B8656 /* Reliability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400540F12325CDF4004B8656 /* Reliability.swift */; };
Expand Down Expand Up @@ -142,6 +143,7 @@

/* Begin PBXFileReference section */
1A4B46EA20AA7717003D5110 /* NSTableView+Amethyst.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Amethyst.swift"; sourceTree = "<group>"; };
1AA5BA9B2D8C53E6004D6976 /* FloatingGridLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingGridLayout.swift; sourceTree = "<group>"; };
2A6D9A4025E5D24D006A36B5 /* AppManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
4000DB0F239CA07000365A0C /* WideLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideLayoutTests.swift; sourceTree = "<group>"; };
400540F12325CDF4004B8656 /* Reliability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reliability.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -362,6 +364,7 @@
children = (
404BE9CD1CFBB6E900D6C537 /* BinarySpacePartitioningLayout.swift */,
4062AD321C1FA48C00DB612B /* ColumnLayout.swift */,
1AA5BA9B2D8C53E6004D6976 /* FloatingGridLayout.swift */,
4062AD2A1C1F99EA00DB612B /* FloatingLayout.swift */,
4062AD2C1C1F9B8B00DB612B /* FullscreenLayout.swift */,
4062AD361C1FA83300DB612B /* RowLayout.swift */,
Expand Down Expand Up @@ -607,7 +610,6 @@
isa = PBXNativeTarget;
buildConfigurationList = 402DB6FB1742E41A00D1C936 /* Build configuration list for PBXNativeTarget "Amethyst" */;
buildPhases = (
4058C4701C4C4F9E00B19D26 /* Lint */,
402DB6DA1742E41A00D1C936 /* Sources */,
402DB6DB1742E41A00D1C936 /* Frameworks */,
402DB6DC1742E41A00D1C936 /* Resources */,
Expand Down Expand Up @@ -754,29 +756,12 @@
};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
4058C4701C4C4F9E00B19D26 /* Lint */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = Lint;
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nswiftlint --fix\nswiftlint\n";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
402DB6DA1742E41A00D1C936 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1AA5BA9C2D8C53E6004D6976 /* FloatingGridLayout.swift in Sources */,
40CF37C229B58C7400CDB07A /* WindowsInfo.swift in Sources */,
40AF71DD20BB401400F58EA9 /* FloatingPreferencesViewController.swift in Sources */,
40A88003240C40BC005EE9C6 /* LayoutsPreferencesViewController.swift in Sources */,
Expand Down Expand Up @@ -1026,7 +1011,7 @@
CURRENT_PROJECT_VERSION = 121;
DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = VKFDYMU9HJ;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you revert these?

ENABLE_HARDENED_RUNTIME = YES;
ENABLE_MODULE_VERIFIER = YES;
FRAMEWORK_SEARCH_PATHS = (
Expand All @@ -1047,7 +1032,7 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"DEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = "com.amethyst.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_BUNDLE_IDENTIFIER = "com.amethyst-mutatingfunc-fork.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "";
Expand Down Expand Up @@ -1091,7 +1076,7 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14";
ONLY_ACTIVE_ARCH = NO;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"RELEASE\"";
PRODUCT_BUNDLE_IDENTIFIER = "com.amethyst.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_BUNDLE_IDENTIFIER = "com.amethyst-mutatingfunc-fork.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ class BinarySpacePartitioningLayout<Window: WindowType>: StatefulLayout<Window>

windowNode.windowID = otherWindowID
otherWindowNode.windowID = windowID
case .applicationDeactivate, .applicationActivate, .spaceChange, .layoutChange, .tabChange, .none, .unknown:
case .resize, .applicationDeactivate, .applicationActivate, .spaceChange, .layoutChange, .tabChange, .none, .unknown:
break
}
}
Expand Down
3 changes: 3 additions & 0 deletions Amethyst/Layout/Layouts/CustomLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ class CustomLayout<Window: WindowType>: StatefulLayout<Window>, PanedLayout {
case .remove(window: let window):
jsChange["change"] = "remove"
jsChange["windowID"] = idHash(forWindowID: window.id())
case .resize(window: let window):
jsChange["change"] = "resize"
jsChange["windowID"] = idHash(forWindowID: window.id())
case .focusChanged(window: let window):
jsChange["change"] = "focus_changed"
jsChange["windowID"] = idHash(forWindowID: window.id())
Expand Down
114 changes: 114 additions & 0 deletions Amethyst/Layout/Layouts/FloatingGridLayout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// FloatingGridLayout.swift
// Amethyst
//
// Created by James Froggatt on 31/10/23.
// Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class FloatingGridLayout<Window: WindowType>: Layout<Window> {
override static var layoutName: String { return "Floating Grid" }
override static var layoutKey: String { return "floating-grid" }

override var layoutDescription: String { return "" }

override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
let screenFrame = screen.adjustedFrame()
let smartWindowMargins = UserConfiguration.shared.smartWindowMargins()
let windowMarginSize = UserConfiguration.shared.windowMarginSize()
let gridGuides = (
horizontal: self.gridGuides(in: screenFrame.width, from: screenFrame.minX),
vertical: self.gridGuides(in: screenFrame.height, from: screenFrame.minY)
)
return windowSet.windows.map { window in
let frameInset: CGFloat = windowMarginSize/2
var frame = window.frame.insetBy(dx: -frameInset, dy: -frameInset)
// Limit windows to screen bounds
// 1. Pull window up left onto screen
if frame.maxX > screenFrame.maxX {
frame.origin.x += screenFrame.maxX - frame.maxX
}
if frame.maxY > screenFrame.maxY {
frame.origin.y += screenFrame.maxY - frame.maxY
}
// 2. Push window down right onto screen
if frame.minX < screenFrame.minX {
frame.origin.x = screenFrame.minX
}
if frame.minY < screenFrame.minY {
frame.origin.y = screenFrame.minY
}
// 3. Trim window's bottom right
if frame.maxX > screenFrame.maxX {
frame.size.width += screenFrame.maxX - frame.maxX
}
if frame.maxY > screenFrame.maxY {
frame.origin.y += screenFrame.maxY - frame.maxY
}
// Align to grid
let oldX = frame.minX
let oldY = frame.minY
frame.origin.x = match(frame.minX, to: gridGuides.horizontal, leadingEdge: true)
frame.origin.y = match(frame.minY, to: gridGuides.vertical, leadingEdge: true)
frame.size.width += oldX - frame.origin.x
frame.size.height += oldY - frame.origin.y
frame.size.width = match(frame.maxX, to: gridGuides.horizontal, leadingEdge: false) - frame.minX
frame.size.height = match(frame.maxY, to: gridGuides.vertical, leadingEdge: false) - frame.minY

let isFullscreen = smartWindowMargins && frame.union(screenFrame) == frame

let resizeRules = ResizeRules(isMain: true, unconstrainedDimension: .horizontal, scaleFactor: 1)
let frameAssignment = FrameAssignment<Window>(
frame: isFullscreen ? screen.adjustedFrame(disableWindowMargins: true) : frame,
window: window,
screenFrame: isFullscreen ? screen.adjustedFrame(disableWindowMargins: true) : screenFrame,
resizeRules: resizeRules,
disableWindowMargins: isFullscreen
)
return FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)
}
}

func gridGuides(in dimension: CGFloat, from origin: CGFloat) -> [CGFloat] {
let baseGridSize: CGFloat = 64
let points = Int(dimension / baseGridSize)
let gridItemSize = dimension / CGFloat(points)
return (0...points).map { CGFloat($0) * gridItemSize + origin }
}

func match(_ value: CGFloat, to guides: [CGFloat], leadingEdge: Bool) -> CGFloat {
var guides = guides
// Prevent offscreen snapping
if leadingEdge {
guides.removeLast()
} else {
guides.removeFirst()
}
// Make it easier to snap to screen edges and avoid stage manager sidebar
if guides.indices.count > 2 {
guides.remove(at: 1)
guides.remove(at: guides.endIndex-2)
}
// Find closest snap guide
let index = guides.firstIndex(where: { $0 >= value }) ?? guides.indices.last!
let previous = guides.indices.contains(index-1) ? guides[index-1] : guides.first!
let next = guides.indices.contains(index) ? guides[index] : guides.last!
if (value - previous) > (next - value) {
return next
} else {
return previous
}
}
}

// To ensure updates when resizing
extension FloatingGridLayout: PanedLayout {
var mainPaneRatio: CGFloat { 0.5 }
var mainPaneCount: Int { 1 }
func recommendMainPaneRawRatio(rawRatio: CGFloat) {}
func increaseMainPaneCount() {}
func decreaseMainPaneCount() {}

}
8 changes: 8 additions & 0 deletions Amethyst/Managers/LayoutType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ enum LayoutType<Window: WindowType> {
case column
case row
case floating
case floatingGrid
case widescreenTallLeft
case widescreenTallRight
case binarySpacePartitioning
Expand All @@ -78,6 +79,7 @@ enum LayoutType<Window: WindowType> {
.column,
.row,
.floating,
.floatingGrid,
.widescreenTallLeft,
.widescreenTallRight,
.binarySpacePartitioning
Expand Down Expand Up @@ -114,6 +116,8 @@ enum LayoutType<Window: WindowType> {
return "row"
case .floating:
return "floating"
case .floatingGrid:
return "floating-grid"
case .widescreenTallLeft:
return "widescreen-tall"
case .widescreenTallRight:
Expand Down Expand Up @@ -155,6 +159,8 @@ enum LayoutType<Window: WindowType> {
return RowLayout<Window>.self
case .floating:
return FloatingLayout<Window>.self
case .floatingGrid:
return FloatingGridLayout<Window>.self
case .widescreenTallLeft:
return WidescreenTallLayoutLeft<Window>.self
case .widescreenTallRight:
Expand Down Expand Up @@ -196,6 +202,8 @@ enum LayoutType<Window: WindowType> {
return .row
case "floating":
return .floating
case "floating-grid":
return .floatingGrid
case "widescreen-tall":
return .widescreenTallLeft
case "widescreen-tall-right":
Expand Down
2 changes: 1 addition & 1 deletion Amethyst/Managers/ScreenManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ final class ScreenManager<Delegate: ScreenManagerDelegate>: NSObject, Codable {
if lastFocusedWindow == window {
lastFocusedWindow = nil
}
case .windowSwap, .applicationActivate, .applicationDeactivate, .spaceChange, .layoutChange, .tabChange, .none, .unknown:
case .resize, .windowSwap, .applicationActivate, .applicationDeactivate, .spaceChange, .layoutChange, .tabChange, .none, .unknown:
break
}

Expand Down
26 changes: 19 additions & 7 deletions Amethyst/Managers/WindowManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,11 @@ extension WindowManager: ApplicationObservationDelegate {
// record window and wait for mouse up
mouseStateKeeper.state = .moving(window: window)
case let .doneDragging(lmbUpMoment):

if let screenManager = focusedScreenManager(), screenManager.currentLayout is FloatingGridLayout {
screenManager.setNeedsReflow(withWindowChange: .resize(window: window))
}

mouseStateKeeper.state = .pointing // flip state first to prevent race condition

// if mouse button recently came up, assume window move is related
Expand All @@ -622,19 +627,25 @@ extension WindowManager: ApplicationObservationDelegate {
}

func application(_ application: AnyApplication<Application>, didResizeWindow window: Window) {
guard userConfiguration.mouseResizesWindows() else {
guard
let screenManager: ScreenManager<WindowManager<Application>> = focusedScreenManager(),
let layout = screenManager.currentLayout
else {
return
}

guard userConfiguration.mouseResizesWindows() || layout is FloatingGridLayout else {
return
}

guard let screen = window.screen(), activeWindows(on: screen).contains(window) else {
return
}

guard
let screenManager: ScreenManager<WindowManager<Application>> = focusedScreenManager(),
let layout = screenManager.currentLayout,
layout is PanedLayout
else {

guard layout is PanedLayout else {
if layout is FloatingGridLayout, case .doneDragging = mouseStateKeeper.state {
screenManager.setNeedsReflow(withWindowChange: .resize(window: window))
}
return
}

Expand Down Expand Up @@ -706,6 +717,7 @@ extension WindowManager: WindowTransitionTarget {
switch transition {
case let .switchWindows(window, otherWindow):
guard windows.swap(window: window, withWindow: otherWindow) else {
markAllScreensForReflow(withChange: .focusChanged(window: window))
return
}

Expand Down
1 change: 1 addition & 0 deletions Amethyst/Model/Change.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Foundation
enum Change<Window: WindowType> {
case add(window: Window)
case remove(window: Window)
case resize(window: Window)
case focusChanged(window: Window)
case windowSwap(window: Window, otherWindow: Window)
case applicationActivate
Expand Down
Loading