From ffc627640426c5b81a093f8a58870366bc382c96 Mon Sep 17 00:00:00 2001 From: Kai Azim <68963405+MrKai77@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:45:40 -0600 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20#165=20Finish=20fully=20removin?= =?UTF-8?q?g=20preset=20cycle=20keybinds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Loop/Managers/LoopManager.swift | 23 +++-- Loop/Preview Window/PreviewController.swift | 1 - .../RadialMenuDirectionSelectorView.swift | 16 ++-- Loop/Radial Menu/RadialMenuView.swift | 4 +- Loop/Settings/RadialMenuSettingsView.swift | 2 - Loop/Window Management/WindowDirection.swift | 94 +++---------------- 6 files changed, 35 insertions(+), 105 deletions(-) diff --git a/Loop/Managers/LoopManager.swift b/Loop/Managers/LoopManager.swift index 89dc7e28..c0f8904d 100644 --- a/Loop/Managers/LoopManager.swift +++ b/Loop/Managers/LoopManager.swift @@ -110,13 +110,13 @@ class LoopManager: ObservableObject { // If mouse over 50 points away, select half or quarter positions if distanceToMouse > pow(50 - Defaults[.radialMenuThickness], 2) { switch Int((angleToMouse.normalized().degrees + 22.5) / 45) { - case 0, 8: resizeDirection = .cycleRight + case 0, 8: resizeDirection = .rightHalf case 1: resizeDirection = .bottomRightQuarter - case 2: resizeDirection = .cycleBottom + case 2: resizeDirection = .bottomHalf case 3: resizeDirection = .bottomLeftQuarter - case 4: resizeDirection = .cycleLeft + case 4: resizeDirection = .leftHalf case 5: resizeDirection = .topLeftQuarter - case 6: resizeDirection = .cycleTop + case 6: resizeDirection = .topHalf case 7: resizeDirection = .topRightQuarter default: resizeDirection = .noAction } @@ -126,7 +126,7 @@ class LoopManager: ObservableObject { resizeDirection = .maximize } - if resizeDirection != self.currentAction.direction.base { + if resizeDirection != self.currentAction.direction { changeAction(.init(resizeDirection)) } } @@ -151,16 +151,19 @@ class LoopManager: ObservableObject { var newAction = action - if newAction.direction.isPresetCyclable { - newAction = .init(newAction.direction.nextCyclingDirection(from: self.currentAction.direction)) - } - if newAction.direction == .cycle { guard let cycle = action.cycle else { return } var nextIndex = (cycle.firstIndex(of: self.currentAction) ?? -1) + 1 + + if self.currentAction.direction != .custom { + nextIndex = (cycle.firstIndex(where: { + $0.direction == self.currentAction.direction + }) ?? -1) + 1 + } + if nextIndex >= cycle.count { nextIndex = 0 } @@ -199,7 +202,7 @@ class LoopManager: ObservableObject { Notification.Name.updateUIDirection.post(userInfo: ["action": self.currentAction]) } - if action.direction.isPresetCyclable || action.direction == .cycle { + if action.direction == .cycle { self.currentAction = newAction self.changeAction(action) } else { diff --git a/Loop/Preview Window/PreviewController.swift b/Loop/Preview Window/PreviewController.swift index 1171641b..32ada990 100644 --- a/Loop/Preview Window/PreviewController.swift +++ b/Loop/Preview Window/PreviewController.swift @@ -85,7 +85,6 @@ class PreviewController { guard let windowController = previewWindowController, let screen = self.screen, - !action.direction.isPresetCyclable, !action.direction.willChangeScreen, action.direction != .cycle else { diff --git a/Loop/Radial Menu/RadialMenuDirectionSelectorView.swift b/Loop/Radial Menu/RadialMenuDirectionSelectorView.swift index 1ccc537a..33c2072f 100644 --- a/Loop/Radial Menu/RadialMenuDirectionSelectorView.swift +++ b/Loop/Radial Menu/RadialMenuDirectionSelectorView.swift @@ -30,17 +30,17 @@ struct RadialMenuDirectionSelectorView: View { HStack(spacing: 0) { VStack(spacing: 0) { DirectionSelectorSquareSegment(.topLeftQuarter, activeAngle, radialMenuSize) - DirectionSelectorSquareSegment(.cycleLeft, activeAngle, radialMenuSize) + DirectionSelectorSquareSegment(.leftHalf, activeAngle, radialMenuSize) DirectionSelectorSquareSegment(.bottomLeftQuarter, activeAngle, radialMenuSize) } VStack(spacing: 0) { - DirectionSelectorSquareSegment(.cycleTop, activeAngle, radialMenuSize) + DirectionSelectorSquareSegment(.topHalf, activeAngle, radialMenuSize) Spacer().frame(width: radialMenuSize/3, height: radialMenuSize/3) - DirectionSelectorSquareSegment(.cycleBottom, activeAngle, radialMenuSize) + DirectionSelectorSquareSegment(.bottomHalf, activeAngle, radialMenuSize) } VStack(spacing: 0) { DirectionSelectorSquareSegment(.topRightQuarter, activeAngle, radialMenuSize) - DirectionSelectorSquareSegment(.cycleRight, activeAngle, radialMenuSize) + DirectionSelectorSquareSegment(.rightHalf, activeAngle, radialMenuSize) DirectionSelectorSquareSegment(.bottomRightQuarter, activeAngle, radialMenuSize) } } @@ -49,13 +49,13 @@ struct RadialMenuDirectionSelectorView: View { // This is used when the user configures the radial menu to be a circle Color.clear .overlay { - DirectionSelectorCircleSegment(.cycleRight, activeAngle, radialMenuSize) + DirectionSelectorCircleSegment(.rightHalf, activeAngle, radialMenuSize) DirectionSelectorCircleSegment(.bottomRightQuarter, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.cycleBottom, activeAngle, radialMenuSize) + DirectionSelectorCircleSegment(.bottomHalf, activeAngle, radialMenuSize) DirectionSelectorCircleSegment(.bottomLeftQuarter, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.cycleLeft, activeAngle, radialMenuSize) + DirectionSelectorCircleSegment(.leftHalf, activeAngle, radialMenuSize) DirectionSelectorCircleSegment(.topLeftQuarter, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.cycleTop, activeAngle, radialMenuSize) + DirectionSelectorCircleSegment(.topHalf, activeAngle, radialMenuSize) DirectionSelectorCircleSegment(.topRightQuarter, activeAngle, radialMenuSize) } } diff --git a/Loop/Radial Menu/RadialMenuView.swift b/Loop/Radial Menu/RadialMenuView.swift index 9d3fbf40..1dd6d6aa 100644 --- a/Loop/Radial Menu/RadialMenuView.swift +++ b/Loop/Radial Menu/RadialMenuView.swift @@ -28,7 +28,7 @@ struct RadialMenuView: View { init(previewMode: Bool = false, window: Window?, startingAction: WindowAction = .init(.noAction)) { self.window = window self.previewMode = previewMode - self._currentAction = State(initialValue: .init(startingAction.direction.base)) + self._currentAction = State(initialValue: .init(startingAction.direction)) if previewMode { self._timer = State(initialValue: Timer.publish(every: 1, on: .main, in: .common).autoconnect()) @@ -113,7 +113,7 @@ struct RadialMenuView: View { } .onReceive(.updateUIDirection) { obj in if !self.previewMode, let action = obj.userInfo?["action"] as? WindowAction { - self.currentAction = .init(action.direction.base) + self.currentAction = .init(action.direction) print("New radial menu window action recieved: \(action.direction)") } diff --git a/Loop/Settings/RadialMenuSettingsView.swift b/Loop/Settings/RadialMenuSettingsView.swift index 4847ef9c..30dd5016 100644 --- a/Loop/Settings/RadialMenuSettingsView.swift +++ b/Loop/Settings/RadialMenuSettingsView.swift @@ -16,8 +16,6 @@ struct RadialMenuSettingsView: View { @Default(.disableCursorInteraction) var disableCursorInteraction @Default(.radialMenuDelay) var radialMenuDelay - @State var currentResizeDirection: WindowDirection = .cycleTop - var body: some View { Form { Section("Appearance") { diff --git a/Loop/Window Management/WindowDirection.swift b/Loop/Window Management/WindowDirection.swift index 015de4cd..e26709f6 100644 --- a/Loop/Window Management/WindowDirection.swift +++ b/Loop/Window Management/WindowDirection.swift @@ -25,12 +25,6 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { case macOSCenter = "MacOSCenter" case center = "Center" - // To cycle through directions - case cycleTop = "CycleTop" - case cycleBottom = "CycleBottom" - case cycleRight = "CycleRight" - case cycleLeft = "CycleLeft" - // Halves case topHalf = "TopHalf" case rightHalf = "RightHalf" @@ -80,9 +74,6 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { static var verticalThirds: [WindowDirection] { [.topThird, .topTwoThirds, .verticalCenterThird, .bottomTwoThirds, .bottomThird] } - static var cyclable: [WindowDirection] { - [.cycleTop, .cycleBottom, .cycleLeft, .cycleRight] - } static var screenSwitching: [WindowDirection] { [.nextScreen, .previousScreen] } @@ -90,10 +81,6 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { [.initialFrame, .undo, .custom, .cycle] } - var isPresetCyclable: Bool { - WindowDirection.cyclable.contains(self) - } - var willChangeScreen: Bool { WindowDirection.screenSwitching.contains(self) } @@ -101,14 +88,14 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { // Used in the settings window to loop over the possible combinations var nextPreviewDirection: WindowDirection { switch self { - case .noAction: .cycleTop - case .cycleTop: .topRightQuarter - case .topRightQuarter: .cycleRight - case .cycleRight: .bottomRightQuarter - case .bottomRightQuarter: .cycleBottom - case .cycleBottom: .bottomLeftQuarter - case .bottomLeftQuarter: .cycleLeft - case .cycleLeft: .topLeftQuarter + case .noAction: .topHalf + case .topHalf: .topRightQuarter + case .topRightQuarter: .rightHalf + case .rightHalf: .bottomRightQuarter + case .bottomRightQuarter: .bottomHalf + case .bottomHalf: .bottomLeftQuarter + case .bottomLeftQuarter: .leftHalf + case .leftHalf: .topLeftQuarter case .topLeftQuarter: .maximize case .maximize: .noAction default: .noAction @@ -117,13 +104,13 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { var radialMenuAngle: Double? { switch self { - case .cycleTop: 0 + case .topHalf: 0 case .topRightQuarter: 45 - case .cycleRight: 90 + case .rightHalf: 90 case .bottomRightQuarter: 135 - case .cycleBottom: 180 + case .bottomHalf: 180 case .bottomLeftQuarter: 225 - case .cycleLeft: 270 + case .leftHalf: 270 case .topLeftQuarter: 315 case .maximize: 0 default: nil @@ -153,13 +140,6 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { var moreInformation: String? { var result: String? - if self.isPresetCyclable { - result = """ - This keybind cycles: press it repeatedly to cycle through - 1/2, 1/3, and 2/3, then move onto the next screen. - """ - } - if self == .macOSCenter { result = """ \(self.name) places windows slightly above the absolute center, @@ -182,11 +162,6 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { case .center: Image("custom.rectangle.center.inset.inset.filled") case .macOSCenter: Image("custom.rectangle.center.inset.inset.filled") - case .cycleTop: Image(systemName: "rectangle.tophalf.inset.filled") - case .cycleBottom: Image(systemName: "rectangle.bottomhalf.inset.filled") - case .cycleRight: Image(systemName: "rectangle.righthalf.inset.filled") - case .cycleLeft: Image(systemName: "rectangle.lefthalf.inset.filled") - case .topHalf: Image(systemName: "rectangle.tophalf.inset.filled") case .rightHalf: Image(systemName: "rectangle.righthalf.inset.filled") case .bottomHalf: Image(systemName: "rectangle.bottomhalf.inset.filled") @@ -359,49 +334,4 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { default: nil } } - - func nextCyclingDirection(from: WindowDirection) -> WindowDirection { - switch self { - case .cycleTop: - switch from { - case .topHalf: return .topThird - case .topThird: return .topTwoThirds - default: return .topHalf - } - case .cycleBottom: - switch from { - case .bottomHalf: return .bottomThird - case .bottomThird: return .bottomTwoThirds - default: return .bottomHalf - } - case .cycleLeft: - switch from { - case .leftHalf: return .leftThird - case .leftThird: return .leftTwoThirds - case .leftTwoThirds: return .previousScreen - case .previousScreen: return .rightHalf - default: return .leftHalf - } - case .cycleRight: - switch from { - case .rightHalf: return .rightThird - case .rightThird: return .rightTwoThirds - case .rightTwoThirds: return .nextScreen - case .nextScreen: return .leftHalf - default: return .rightHalf - } - default: return .noAction - } - } - - // Gets the cyclable direction - var base: WindowDirection { - switch self { - case .topHalf, .topThird, .topTwoThirds: .cycleTop - case .bottomHalf, .bottomThird, .bottomTwoThirds: .cycleBottom - case .leftHalf, .leftThird, .leftTwoThirds: .cycleLeft - case .rightHalf, .rightThird, .rightTwoThirds: .cycleRight - default: self - } - } } From 7587f095696fe1a5ec2d0ae79426a782f48fa7b2 Mon Sep 17 00:00:00 2001 From: Kai Azim <68963405+MrKai77@users.noreply.github.com> Date: Wed, 27 Mar 2024 21:48:45 -0600 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20Make=20radial=20menu=20work=20o?= =?UTF-8?q?n=20more=20cycling=20keybinds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Loop/Extensions/Angle+Extensions.swift | 8 ++- .../DirectionSelectorCircleSegment.swift | 56 +++++++++---------- Loop/Radial Menu/RadialMenuView.swift | 48 ++++++++++++++-- Loop/Utilities/AnimationConfiguration.swift | 8 ++- Loop/Window Management/WindowAction.swift | 16 ++++++ Loop/Window Management/WindowDirection.swift | 36 +++++++++++- 6 files changed, 130 insertions(+), 42 deletions(-) diff --git a/Loop/Extensions/Angle+Extensions.swift b/Loop/Extensions/Angle+Extensions.swift index 3d212703..a7bbee43 100644 --- a/Loop/Extensions/Angle+Extensions.swift +++ b/Loop/Extensions/Angle+Extensions.swift @@ -7,7 +7,6 @@ import SwiftUI -// Returns an Angle in the range 0° ..< 360° extension Angle { func normalized() -> Angle { let degrees = (self.degrees.truncatingRemainder(dividingBy: 360) + 360) @@ -15,4 +14,11 @@ extension Angle { return Angle(degrees: degrees) } + + func angleDifference(to angle2: Angle) -> Angle { + let angle1 = self.degrees + let angle2 = angle2.degrees + let diff: Double = ( angle2 - angle1 + 180.0 ).truncatingRemainder(dividingBy: 360.0) - 180.0 + return Angle(degrees: diff < -180 ? diff + 360 : diff) + } } diff --git a/Loop/Radial Menu/DirectionSelectorCircleSegment.swift b/Loop/Radial Menu/DirectionSelectorCircleSegment.swift index 38349160..6dc2922a 100644 --- a/Loop/Radial Menu/DirectionSelectorCircleSegment.swift +++ b/Loop/Radial Menu/DirectionSelectorCircleSegment.swift @@ -7,40 +7,34 @@ import SwiftUI -struct DirectionSelectorCircleSegment: View { - var startingAngle: Double = 0 - let isActive: Bool +struct DirectionSelectorCircleSegment: Shape { + let radialMenuSize: CGFloat + var angle: Double = .zero - init(_ resizePosition: WindowDirection, _ activeResizePosition: WindowDirection, _ radialMenuSize: CGFloat) { - if let angle = resizePosition.radialMenuAngle { - self.startingAngle = angle - 90 - 22.5 - } - if resizePosition == activeResizePosition { - isActive = true - } else { - isActive = false - } - self.radialMenuSize = radialMenuSize + var animatableData: Double { + get { self.angle } + set { self.angle = newValue } } - var body: some View { - Path { path in - path.move(to: - CGPoint( - x: radialMenuSize/2, - y: radialMenuSize/2 - ) - ) - path.addArc( - center: CGPoint(x: radialMenuSize/2, - y: radialMenuSize/2), - radius: radialMenuSize, - startAngle: .degrees(startingAngle), - endAngle: .degrees(startingAngle+45), - clockwise: false - ) - } - .foregroundColor(isActive ? Color.black : Color.clear) + func path(in rect: CGRect) -> Path { + var path = Path() + + path.move(to: + CGPoint( + x: radialMenuSize/2, + y: radialMenuSize/2 + ) + ) + path.addArc( + center: CGPoint(x: radialMenuSize/2, + y: radialMenuSize/2), + radius: radialMenuSize, + startAngle: .degrees(angle - 22.5), + endAngle: .degrees(angle + 22.5), + clockwise: false + ) + + return path } } diff --git a/Loop/Radial Menu/RadialMenuView.swift b/Loop/Radial Menu/RadialMenuView.swift index 1dd6d6aa..f610cc60 100644 --- a/Loop/Radial Menu/RadialMenuView.swift +++ b/Loop/Radial Menu/RadialMenuView.swift @@ -14,6 +14,8 @@ struct RadialMenuView: View { let radialMenuSize: CGFloat = 100 @State var currentAction: WindowAction + @State var previousAction: WindowAction? + private let window: Window? private let previewMode: Bool @@ -37,6 +39,8 @@ struct RadialMenuView: View { } } + @State var angle: Double = .zero + var body: some View { VStack { Spacer() @@ -63,10 +67,24 @@ struct RadialMenuView: View { ) ) .mask { - RadialMenuDirectionSelectorView( - activeAngle: currentAction.direction, - size: self.radialMenuSize - ) + Color.clear + .overlay { + ZStack { + if self.currentAction.direction.shouldFillRadialMenu { + Color.white + } + + DirectionSelectorCircleSegment( + radialMenuSize: self.radialMenuSize, + angle: self.angle + ) + .opacity( + !self.currentAction.direction.hasRadialMenuAngle || + self.currentAction.direction == .custom ? + 0 : 1 + ) + } + } } } // Mask the whole ZStack with the shape the user defines @@ -100,7 +118,7 @@ struct RadialMenuView: View { // Animate window .scaleEffect(currentAction.direction == .maximize ? 0.85 : 1) - .animation(animationConfiguration.radialMenuAnimation, value: currentAction) + .animation(animationConfiguration.radialMenuSize, value: currentAction) .onAppear { if previewMode { currentAction.direction = currentAction.direction.nextPreviewDirection @@ -108,15 +126,35 @@ struct RadialMenuView: View { } .onReceive(timer) { _ in if previewMode { + previousAction = currentAction currentAction.direction = currentAction.direction.nextPreviewDirection } } .onReceive(.updateUIDirection) { obj in if !self.previewMode, let action = obj.userInfo?["action"] as? WindowAction { + self.previousAction = self.currentAction self.currentAction = .init(action.direction) print("New radial menu window action recieved: \(action.direction)") } } + .onChange(of: self.currentAction) { _ in + if let target = self.currentAction.radialMenuAngle(window: window) { + let closestAngle: Angle = .degrees(self.angle).angleDifference(to: target) + + let previousActionHadAngle = self.previousAction?.direction.hasRadialMenuAngle ?? false + let animate: Bool = abs(closestAngle.degrees) < 179 && previousActionHadAngle + + print(animate + ) + + let defaultAnimation = AnimationConfiguration.fast.radialMenuAngle + let noAnimation = Animation.linear(duration: 0) + + withAnimation(animate ? defaultAnimation : noAnimation) { + self.angle += closestAngle.degrees + } + } + } } } diff --git a/Loop/Utilities/AnimationConfiguration.swift b/Loop/Utilities/AnimationConfiguration.swift index da74143b..6c5c37c5 100644 --- a/Loop/Utilities/AnimationConfiguration.swift +++ b/Loop/Utilities/AnimationConfiguration.swift @@ -31,11 +31,15 @@ enum AnimationConfiguration: Int, Defaults.Serializable, CaseIterable, Identifia } } - var radialMenuAnimation: Animation { + var radialMenuSize: Animation { switch self { - case .smooth: .easeOut + case .smooth: .easeOut(duration: 0.2) case .fast: .easeOut(duration: 0.2) case .instant: .easeOut(duration: 0.1) } } + + var radialMenuAngle: Animation { + Animation.timingCurve(0.22, 1, 0.36, 1, duration: 0.2) + } } diff --git a/Loop/Window Management/WindowAction.swift b/Loop/Window Management/WindowAction.swift index 6b47c7bf..441c927b 100644 --- a/Loop/Window Management/WindowAction.swift +++ b/Loop/Window Management/WindowAction.swift @@ -94,6 +94,22 @@ struct WindowAction: Codable, Identifiable, Hashable, Equatable, Defaults.Serial return result } + func radialMenuAngle(window: Window?) -> Angle? { + guard + direction.frameMultiplyValues != nil, + direction.hasRadialMenuAngle + else { + return nil + } + + let frame = CGRect(origin: .zero, size: .init(width: 1, height: 1)) + let targetWindowFrame = getFrame(window: window, bounds: frame) + let angle = frame.center.angle(to: targetWindowFrame.center) + let result: Angle = .radians(angle) * -1 + + return result.normalized() + } + func getFrame(window: Window?, bounds: CGRect) -> CGRect { guard self.direction != .cycle, self.direction != .noAction else { return NSRect(origin: bounds.center, size: .zero) diff --git a/Loop/Window Management/WindowDirection.swift b/Loop/Window Management/WindowDirection.swift index e26709f6..a1cd5310 100644 --- a/Loop/Window Management/WindowDirection.swift +++ b/Loop/Window Management/WindowDirection.swift @@ -88,7 +88,6 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { // Used in the settings window to loop over the possible combinations var nextPreviewDirection: WindowDirection { switch self { - case .noAction: .topHalf case .topHalf: .topRightQuarter case .topRightQuarter: .rightHalf case .rightHalf: .bottomRightQuarter @@ -97,8 +96,7 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { case .bottomLeftQuarter: .leftHalf case .leftHalf: .topLeftQuarter case .topLeftQuarter: .maximize - case .maximize: .noAction - default: .noAction + default: .topHalf } } @@ -117,6 +115,38 @@ enum WindowDirection: String, CaseIterable, Identifiable, Codable { } } + var hasRadialMenuAngle: Bool { + let noAngleActions: [WindowDirection] = [ + .noAction, + .maximize, + .center, + .macOSCenter, + .almostMaximize, + .nextScreen, + .previousScreen, + .fullscreen, + .minimize, + .hide, + .initialFrame, + .undo, + .cycle + ] + + return !noAngleActions.contains(self) + } + + var shouldFillRadialMenu: Bool { + let fillActions: [WindowDirection] = [ + .maximize, + .center, + .macOSCenter, + .almostMaximize, + .fullscreen + ] + + return fillActions.contains(self) + } + var name: String { var result = "" for (idx, char) in self.rawValue.enumerated() { From e9a8f2333e598d75101c651a9465d363b673f58b Mon Sep 17 00:00:00 2001 From: Kai Azim <68963405+MrKai77@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:52:04 -0600 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=A8=20Use=20`trim`=20for=20rounded=20?= =?UTF-8?q?rectangle=20radial=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Loop.xcodeproj/project.pbxproj | 4 -- .../DirectionSelectorCircleSegment.swift | 2 +- .../DirectionSelectorSquareSegment.swift | 32 ++++++---- Loop/Radial Menu/RadialMenuController.swift | 1 + .../RadialMenuDirectionSelectorView.swift | 64 ------------------- Loop/Radial Menu/RadialMenuView.swift | 38 +++++++++-- 6 files changed, 52 insertions(+), 89 deletions(-) delete mode 100644 Loop/Radial Menu/RadialMenuDirectionSelectorView.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 376e8f1b..26e7c9a0 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -73,7 +73,6 @@ A8BE09DB2B113FD700DBB242 /* KeycorderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8BE09DA2B113FD700DBB242 /* KeycorderModel.swift */; }; A8D5A7D62A91384D004EA5BB /* DirectionSelectorSquareSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D5A7D52A91384D004EA5BB /* DirectionSelectorSquareSegment.swift */; }; A8D5A7D82A913862004EA5BB /* DirectionSelectorCircleSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D5A7D72A913862004EA5BB /* DirectionSelectorCircleSegment.swift */; }; - A8D5A7DA2A913B4C004EA5BB /* RadialMenuDirectionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D5A7D92A913B4C004EA5BB /* RadialMenuDirectionSelectorView.swift */; }; A8D6D2FF2B6C87F80061B11F /* PaddingConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D6D2FE2B6C87F80061B11F /* PaddingConfigurationView.swift */; }; A8D6D3012B6C894C0061B11F /* PaddingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D6D3002B6C894C0061B11F /* PaddingModel.swift */; }; A8D6D3032B6C8D750061B11F /* PaddingPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D6D3022B6C8D750061B11F /* PaddingPreviewView.swift */; }; @@ -160,7 +159,6 @@ A8BE09DA2B113FD700DBB242 /* KeycorderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeycorderModel.swift; sourceTree = ""; }; A8D5A7D52A91384D004EA5BB /* DirectionSelectorSquareSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectionSelectorSquareSegment.swift; sourceTree = ""; }; A8D5A7D72A913862004EA5BB /* DirectionSelectorCircleSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectionSelectorCircleSegment.swift; sourceTree = ""; }; - A8D5A7D92A913B4C004EA5BB /* RadialMenuDirectionSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadialMenuDirectionSelectorView.swift; sourceTree = ""; }; A8D6D2FE2B6C87F80061B11F /* PaddingConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingConfigurationView.swift; sourceTree = ""; }; A8D6D3002B6C894C0061B11F /* PaddingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingModel.swift; sourceTree = ""; }; A8D6D3022B6C8D750061B11F /* PaddingPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaddingPreviewView.swift; sourceTree = ""; }; @@ -345,7 +343,6 @@ children = ( A8E59C49297F98670064D4BA /* RadialMenuController.swift */, A8789F6629805B190040512E /* RadialMenuView.swift */, - A8D5A7D92A913B4C004EA5BB /* RadialMenuDirectionSelectorView.swift */, A8D5A7D52A91384D004EA5BB /* DirectionSelectorSquareSegment.swift */, A8D5A7D72A913862004EA5BB /* DirectionSelectorCircleSegment.swift */, ); @@ -602,7 +599,6 @@ A8789F6929805B340040512E /* PreviewView.swift in Sources */, A84497C92B393595003D4CF3 /* CustomKeybindView.swift in Sources */, A882660829809F6F00BCB197 /* GeneralSettingsView.swift in Sources */, - A8D5A7DA2A913B4C004EA5BB /* RadialMenuDirectionSelectorView.swift in Sources */, A8E59C50298045D90064D4BA /* PreviewController.swift in Sources */, A81989062AC8EDB300EFF7A1 /* MenuBarHeaderText.swift in Sources */, A86CB7332A3D22E7006A78F2 /* WindowEngine.swift in Sources */, diff --git a/Loop/Radial Menu/DirectionSelectorCircleSegment.swift b/Loop/Radial Menu/DirectionSelectorCircleSegment.swift index 6dc2922a..e49cbcc6 100644 --- a/Loop/Radial Menu/DirectionSelectorCircleSegment.swift +++ b/Loop/Radial Menu/DirectionSelectorCircleSegment.swift @@ -9,8 +9,8 @@ import SwiftUI struct DirectionSelectorCircleSegment: Shape { - let radialMenuSize: CGFloat var angle: Double = .zero + let radialMenuSize: CGFloat var animatableData: Double { get { self.angle } diff --git a/Loop/Radial Menu/DirectionSelectorSquareSegment.swift b/Loop/Radial Menu/DirectionSelectorSquareSegment.swift index 54cdba43..0920d5af 100644 --- a/Loop/Radial Menu/DirectionSelectorSquareSegment.swift +++ b/Loop/Radial Menu/DirectionSelectorSquareSegment.swift @@ -8,21 +8,27 @@ import SwiftUI struct DirectionSelectorSquareSegment: View { - let isActive: Bool - let radialMenuSize: CGFloat - init(_ resizePosition: WindowDirection, _ activeResizePosition: WindowDirection, _ radialMenuSize: CGFloat) { - if resizePosition == activeResizePosition { - isActive = true - } else { - isActive = false - } - self.radialMenuSize = radialMenuSize - } + var angle: Double = .zero + let radialMenuCornerRadius: CGFloat + let radialMenuThickness: CGFloat var body: some View { - Rectangle() - .foregroundColor(isActive ? Color.black : Color.clear) - .frame(width: radialMenuSize/3, height: radialMenuSize/3) + ZStack { + RoundedRectangle(cornerRadius: radialMenuCornerRadius, style: .continuous) + .trim( + from: (Angle(degrees: self.angle - 22.5).normalized().degrees / 360.0), + to: (Angle(degrees: self.angle + 22.5).normalized().degrees / 360.0) + ) + .stroke(.white, lineWidth: radialMenuThickness * 2) + + RoundedRectangle(cornerRadius: radialMenuCornerRadius, style: .continuous) + .trim( + from: (Angle(degrees: self.angle - 180 - 22.5).normalized().degrees / 360.0), + to: (Angle(degrees: self.angle - 180 + 22.5).normalized().degrees / 360.0) + ) + .stroke(.white, lineWidth: radialMenuThickness * 2) + .rotationEffect(.degrees(180)) + } } } diff --git a/Loop/Radial Menu/RadialMenuController.swift b/Loop/Radial Menu/RadialMenuController.swift index 79823538..a94e0aeb 100644 --- a/Loop/Radial Menu/RadialMenuController.swift +++ b/Loop/Radial Menu/RadialMenuController.swift @@ -28,6 +28,7 @@ class RadialMenuController { backing: .buffered, defer: true, screen: NSApp.keyWindow?.screen) + panel.collectionBehavior = .canJoinAllSpaces panel.hasShadow = false panel.backgroundColor = NSColor.white.withAlphaComponent(0.00001) diff --git a/Loop/Radial Menu/RadialMenuDirectionSelectorView.swift b/Loop/Radial Menu/RadialMenuDirectionSelectorView.swift deleted file mode 100644 index 33c2072f..00000000 --- a/Loop/Radial Menu/RadialMenuDirectionSelectorView.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// RadialMenuDirectionSelector.swift -// Loop -// -// Created by Kai Azim on 2023-08-19. -// - -import SwiftUI -import Defaults - -struct RadialMenuDirectionSelectorView: View { - @Default(.radialMenuCornerRadius) var radialMenuCornerRadius - - let activeAngle: WindowDirection - let radialMenuSize: CGFloat - - init(activeAngle: WindowDirection, size: CGFloat) { - self.activeAngle = activeAngle - self.radialMenuSize = size - } - - var body: some View { - if activeAngle == .maximize { - Color.white - } else { - if radialMenuCornerRadius < 40 { - // This is used when the user configures the radial menu to be a square - Color.clear - .overlay { - HStack(spacing: 0) { - VStack(spacing: 0) { - DirectionSelectorSquareSegment(.topLeftQuarter, activeAngle, radialMenuSize) - DirectionSelectorSquareSegment(.leftHalf, activeAngle, radialMenuSize) - DirectionSelectorSquareSegment(.bottomLeftQuarter, activeAngle, radialMenuSize) - } - VStack(spacing: 0) { - DirectionSelectorSquareSegment(.topHalf, activeAngle, radialMenuSize) - Spacer().frame(width: radialMenuSize/3, height: radialMenuSize/3) - DirectionSelectorSquareSegment(.bottomHalf, activeAngle, radialMenuSize) - } - VStack(spacing: 0) { - DirectionSelectorSquareSegment(.topRightQuarter, activeAngle, radialMenuSize) - DirectionSelectorSquareSegment(.rightHalf, activeAngle, radialMenuSize) - DirectionSelectorSquareSegment(.bottomRightQuarter, activeAngle, radialMenuSize) - } - } - } - } else { - // This is used when the user configures the radial menu to be a circle - Color.clear - .overlay { - DirectionSelectorCircleSegment(.rightHalf, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.bottomRightQuarter, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.bottomHalf, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.bottomLeftQuarter, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.leftHalf, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.topLeftQuarter, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.topHalf, activeAngle, radialMenuSize) - DirectionSelectorCircleSegment(.topRightQuarter, activeAngle, radialMenuSize) - } - } - } - } -} diff --git a/Loop/Radial Menu/RadialMenuView.swift b/Loop/Radial Menu/RadialMenuView.swift index f610cc60..98e7ea5a 100644 --- a/Loop/Radial Menu/RadialMenuView.swift +++ b/Loop/Radial Menu/RadialMenuView.swift @@ -74,10 +74,21 @@ struct RadialMenuView: View { Color.white } - DirectionSelectorCircleSegment( - radialMenuSize: self.radialMenuSize, - angle: self.angle - ) + ZStack { + if radialMenuCornerRadius >= radialMenuSize / 2 - 2 { + DirectionSelectorCircleSegment( + angle: self.angle, + radialMenuSize: self.radialMenuSize + ) + } else { + DirectionSelectorSquareSegment( + angle: self.angle, + radialMenuCornerRadius: self.radialMenuCornerRadius, + radialMenuThickness: self.radialMenuThickness + ) + } + } + .compositingGroup() .opacity( !self.currentAction.direction.hasRadialMenuAngle || self.currentAction.direction == .custom ? @@ -86,6 +97,22 @@ struct RadialMenuView: View { } } } + + if radialMenuCornerRadius >= radialMenuSize / 2 - 2 { + Circle() + .stroke(.quinary, lineWidth: 2) + + Circle() + .stroke(.quinary, lineWidth: 2) + .padding(self.radialMenuThickness) + } else { + RoundedRectangle(cornerRadius: radialMenuCornerRadius, style: .continuous) + .stroke(.quinary, lineWidth: 2) + + RoundedRectangle(cornerRadius: radialMenuCornerRadius - self.radialMenuThickness, style: .continuous) + .stroke(.quinary, lineWidth: 2) + .padding(self.radialMenuThickness) + } } // Mask the whole ZStack with the shape the user defines .mask { @@ -145,9 +172,6 @@ struct RadialMenuView: View { let previousActionHadAngle = self.previousAction?.direction.hasRadialMenuAngle ?? false let animate: Bool = abs(closestAngle.degrees) < 179 && previousActionHadAngle - print(animate - ) - let defaultAnimation = AnimationConfiguration.fast.radialMenuAngle let noAnimation = Animation.linear(duration: 0) From 38bf159b5cb8b8548003a509a35f590e6d57ab64 Mon Sep 17 00:00:00 2001 From: Kai Azim <68963405+MrKai77@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:19:31 -0600 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=A8=20Use=20`continuous`=20RoundedRec?= =?UTF-8?q?tangle=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Loop/Preview Window/PreviewView.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Loop/Preview Window/PreviewView.swift b/Loop/Preview Window/PreviewView.swift index dc24da44..3c187fc3 100644 --- a/Loop/Preview Window/PreviewView.swift +++ b/Loop/Preview Window/PreviewView.swift @@ -32,9 +32,12 @@ struct PreviewView: View { GeometryReader { _ in ZStack { VisualEffectView(material: .hudWindow, blendingMode: .behindWindow) - .mask(RoundedRectangle(cornerRadius: previewCornerRadius).foregroundColor(.white)) -// .shadow(radius: 10) - RoundedRectangle(cornerRadius: previewCornerRadius) + .mask { + RoundedRectangle(cornerRadius: previewCornerRadius, style: .continuous) + .foregroundColor(.white) + } + + RoundedRectangle(cornerRadius: previewCornerRadius, style: .continuous) .stroke( LinearGradient( gradient: Gradient(