Skip to content

Commit

Permalink
🔀 Merge pull request #64 from `MrKai77/62-ability-to-hideminimize-win…
Browse files Browse the repository at this point in the history
…dow-from-loop`

✨ #62 Ability to hide/minimize window from Loop
  • Loading branch information
MrKai77 authored Oct 25, 2023
2 parents 61e86ee + 8635519 commit 9261020
Show file tree
Hide file tree
Showing 38 changed files with 541 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@
isa = PBXGroup;
children = (
A848D8A62A8C2F3F00060834 /* LoopManager.swift */,
A864F4672AA660CD00579738 /* SnappingManager.swift */,
A8A2ABE62A3FB0370067B5A9 /* KeybindMonitor.swift */,
A864F4672AA660CD00579738 /* SnappingManager.swift */,
A82521ED29E235AC00139654 /* PermissionsManager.swift */,
A8EF1F08299C87DF00633440 /* IconManager.swift */,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"symbols" : [
{
"filename" : "custom.computermouse.badge.arrow.down.rectangle.fill.svg",
"idiom" : "universal"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"symbols" : [
{
"filename" : "custom.arrow.down.right.and.arrow.up.left.rectangle.svg",
"idiom" : "universal"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"symbols" : [
{
"filename" : "custom.rectangle.slash.svg",
"idiom" : "universal"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions Loop/Extensions/Defaults+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ extension Defaults.Keys {
static let previewPadding = Key<CGFloat>("previewPadding", default: 10)
static let previewBorderThickness = Key<CGFloat>("previewBorderThickness", default: 5)

static let preferMinimizeWithScrollDown = Key<Bool>("preferMinimizeWithScrollDown", default: false)

static let maximizeKeybind = Key<[Set<CGKeyCode>]>(
"maximizeKeybind",
default: [[.kVK_Space]]
Expand All @@ -53,6 +55,14 @@ extension Defaults.Keys {
"lastDirectionKeybind",
default: [[.kVK_ANSI_Z]]
)
static let hideKeybind = Key<[Set<CGKeyCode>]>(
"hideKeybind",
default: [[.kVK_ANSI_H]]
)
static let minimizeKeybind = Key<[Set<CGKeyCode>]>(
"minimizeKeybind",
default: [[.kVK_ANSI_M]]
)

// Cycle Halves
static let cycleTopKeybind = Key<[Set<CGKeyCode>]>(
Expand Down
2 changes: 1 addition & 1 deletion Loop/Managers/KeybindMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class KeybindMonitor {
}

self.shiftKeyEventMonitor = CGEventMonitor(eventMask: .flagsChanged) { cgEvent in
if (cgEvent.type == .flagsChanged),
if cgEvent.type == .flagsChanged,
let event = NSEvent(cgEvent: cgEvent),
event.keyCode != Defaults[.triggerKey].keycode {

Expand Down
49 changes: 39 additions & 10 deletions Loop/Managers/LoopManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ class LoopManager {
private var frontmostWindow: Window?
private var screenWithMouse: NSScreen?

private var flagsChangedEventMonitor: NSEventMonitor?
private var keyDownEventMonitor: NSEventMonitor?
private var flagsChangedEventMonitor: EventMonitor?
private var keyDownEventMonitor: EventMonitor?
private var scrollEventMonitor: EventMonitor?
private var triggerDelayTimer: DispatchSourceTimer?
private var lastTriggerKeyClick: Date = Date.now

Expand All @@ -40,6 +41,32 @@ class LoopManager {
}
self.keyDownEventMonitor!.start()

self.scrollEventMonitor = CGEventMonitor(eventMask: [.scrollWheel]) { cgEvent in
if cgEvent.type == .scrollWheel, self.isLoopShown, let event = NSEvent(cgEvent: cgEvent) {

if Defaults[.preferMinimizeWithScrollDown] {
if event.deltaY > 1 && self.currentResizingDirection != .minimize {
Notification.Name.directionChanged.post(userInfo: ["direction": WindowDirection.minimize])
}

if event.deltaY < -1 && self.currentResizingDirection == .minimize {
Notification.Name.directionChanged.post(userInfo: ["direction": WindowDirection.noAction])
}
} else {
if event.deltaY > 1 && self.currentResizingDirection != .hide {
Notification.Name.directionChanged.post(userInfo: ["direction": WindowDirection.hide])
}

if event.deltaY < -1 && self.currentResizingDirection == .hide {
Notification.Name.directionChanged.post(userInfo: ["direction": WindowDirection.noAction])
}
}

return nil
}
return Unmanaged.passUnretained(cgEvent)
}

Notification.Name.directionChanged.onRecieve { notification in
self.currentWindowDirectionChanged(notification)
}
Expand Down Expand Up @@ -137,23 +164,25 @@ class LoopManager {
self.frontmostWindow = WindowEngine.frontmostWindow
self.screenWithMouse = NSScreen.screenWithMouse

if Defaults[.previewVisibility] == true && frontmostWindow != nil {
previewController.open(screen: self.screenWithMouse!, window: frontmostWindow)
if Defaults[.previewVisibility] == true && self.frontmostWindow != nil {
self.previewController.open(screen: self.screenWithMouse!, window: frontmostWindow)
}
radialMenuController.open(frontmostWindow: frontmostWindow)
keybindMonitor.start()
self.radialMenuController.open(frontmostWindow: frontmostWindow)
self.keybindMonitor.start()
self.scrollEventMonitor?.start()

isLoopShown = true
}
}

private func closeLoop(forceClose: Bool = false) {
self.cancelTriggerDelayTimer()
radialMenuController.close()
previewController.close()
self.radialMenuController.close()
self.previewController.close()

keybindMonitor.resetPressedKeys()
keybindMonitor.stop()
self.keybindMonitor.resetPressedKeys()
self.keybindMonitor.stop()
self.scrollEventMonitor?.stop()

if self.frontmostWindow != nil &&
self.screenWithMouse != nil &&
Expand Down
2 changes: 1 addition & 1 deletion Loop/MenuBar/MenuBarResizeButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct MenuBarResizeButton: View {
}
} label: {
HStack {
if let image = direction.image {
if let image = direction.menuBarImage {
image
}
Text(name)
Expand Down
26 changes: 16 additions & 10 deletions Loop/Preview Window/PreviewView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ struct PreviewView: View {
.bottomThird,
.bottomTwoThirds,
.noAction,
.lastDirection:
.lastDirection,
.hide:
Rectangle()
.frame(height: currentResizeDirection == .bottomThird ? geo.size.height / 3 * 2 : nil)
default:
Expand All @@ -59,7 +60,8 @@ struct PreviewView: View {
.rightThird,
.rightTwoThirds,
.noAction,
.lastDirection:
.lastDirection,
.hide:
Rectangle()
.frame(width: currentResizeDirection == .rightThird ? geo.size.width / 3 * 2 : nil)
default:
Expand Down Expand Up @@ -90,15 +92,16 @@ struct PreviewView: View {
height: currentResizeDirection == .noAction ? 0 : nil)
.frame(width: currentResizeDirection == .initialFrame ? 0 : nil,
height: currentResizeDirection == .initialFrame ? 0 : nil)
.frame(width: currentResizeDirection == .lastDirection ? 0 : nil,
height: currentResizeDirection == .lastDirection ? 0 : nil)
.frame(width: currentResizeDirection == .hide ? 0 : nil,
height: currentResizeDirection == .hide ? 0 : nil)

.frame(width: currentResizeDirection == .center ?
(window?.size.width ?? 10) - previewPadding + previewBorderThickness / 2 :
nil,
(window?.size.width ?? 10) - previewPadding + previewBorderThickness / 2 : nil,
height: currentResizeDirection == .center ?
(window?.size.height ?? 10) - previewPadding + previewBorderThickness / 2 :
nil
(window?.size.height ?? 10) - previewPadding + previewBorderThickness / 2 : nil
)
.frame(width: currentResizeDirection == .lastDirection ? 0 : nil,
height: currentResizeDirection == .lastDirection ? 0 : nil)
.frame(height: currentResizeDirection == .topTwoThirds ? geo.size.height / 3 * 2 : nil)
.frame(height: currentResizeDirection == .bottomTwoThirds ? geo.size.height / 3 * 2 : nil)
.frame(width: currentResizeDirection == .rightTwoThirds ? geo.size.width / 3 * 2 : nil)
Expand All @@ -113,7 +116,8 @@ struct PreviewView: View {
.leftThird,
.leftTwoThirds,
.noAction,
.lastDirection:
.lastDirection,
.hide:
Rectangle()
.frame(width: currentResizeDirection == .leftThird ? geo.size.width / 3 * 2 : nil)
default:
Expand All @@ -130,7 +134,8 @@ struct PreviewView: View {
.topThird,
.topTwoThirds,
.noAction,
.lastDirection:
.lastDirection,
.hide:
Rectangle()
.frame(height: currentResizeDirection == .topThird ? geo.size.height / 3 * 2 : nil)
default:
Expand All @@ -140,6 +145,7 @@ struct PreviewView: View {
}
.foregroundColor(.clear)
.opacity(currentResizeDirection == .noAction ? 0 : 1)
.opacity(currentResizeDirection == .hide ? 0 : 1)
.animation(.interpolatingSpring(stiffness: 250, damping: 25), value: currentResizeDirection)
.onReceive(.directionChanged) { obj in
if let direction = obj.userInfo?["direction"] as? WindowDirection, !self.previewMode, !direction.cyclable {
Expand Down
12 changes: 8 additions & 4 deletions Loop/Radial Menu/RadialMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,15 @@ struct RadialMenuView: View {
}
}

if frontmostWindow == nil && previewMode == false {
Image("custom.macwindow.trianglebadge.exclamationmark")
.foregroundStyle(Color.getLoopAccent(tone: .normal))
.font(Font.system(size: 20, weight: .bold))
Group {
if frontmostWindow == nil && previewMode == false {
Image("custom.macwindow.trianglebadge.exclamationmark")
} else if let image = self.currentResizeDirection.radialMenuImage {
image
}
}
.foregroundStyle(Color.getLoopAccent(tone: .normal))
.font(Font.system(size: 20, weight: .bold))
}
.frame(width: radialMenuSize, height: radialMenuSize)

Expand Down
28 changes: 27 additions & 1 deletion Loop/Settings/KeybindingsSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct KeybindingsSettingsView: View {
@Default(.triggerDelay) var triggerDelay
@Default(.useSystemAccentColor) var useSystemAccentColor
@Default(.customAccentColor) var customAccentColor
@Default(.preferMinimizeWithScrollDown) var preferMinimizeWithScrollDown

@State var suggestAddingTriggerDelay: Bool = false
@State var suggestDisablingCapsLock: Bool = false
Expand Down Expand Up @@ -190,7 +191,7 @@ struct KeybindingsSettingsView: View {

HStack {
VStack(alignment: .leading) {
Text("Use Z undo window operations:")
Text("Press Z undo window operations:")
}

Spacer()
Expand All @@ -208,6 +209,31 @@ struct KeybindingsSettingsView: View {
}
.foregroundStyle(Color.accentColor)
}

HStack {
VStack(alignment: .leading) {
if self.preferMinimizeWithScrollDown {
Text("Scroll down to minimize a window:")
} else {
Text("Scroll down to hide a window:")
}
}

Spacer()

HStack {
Image(self.triggerKey.keySymbol)
.font(Font.system(size: 30, weight: .regular))

Image(systemName: "plus")
.font(Font.system(size: 15, weight: .bold))

Image(systemName: "arrow.up.and.down.square.fill")
.font(Font.system(size: 30, weight: .regular))
.frame(width: 60)
}
.foregroundStyle(Color.accentColor)
}
}
.symbolRenderingMode(.hierarchical)
}
Expand Down
5 changes: 5 additions & 0 deletions Loop/Settings/MoreSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Defaults
struct MoreSettingsView: View {

@EnvironmentObject var updater: SoftwareUpdater
@Default(.preferMinimizeWithScrollDown) var preferMinimizeWithScrollDown

var body: some View {
Form {
Expand All @@ -36,6 +37,10 @@ struct MoreSettingsView: View {
.foregroundStyle(Color.accentColor)
}
})

Section("Extra Settings") {
Toggle("Prefer scroll down to minimize window", isOn: self.$preferMinimizeWithScrollDown)
}
}
.formStyle(.grouped)
.scrollDisabled(true)
Expand Down
9 changes: 7 additions & 2 deletions Loop/Utilities/EventMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

import Cocoa

class NSEventMonitor {
protocol EventMonitor {
func start()
func stop()
}

class NSEventMonitor: EventMonitor {
private var localEventMonitor: Any?
private var globalEventMonitor: Any?

Expand Down Expand Up @@ -51,7 +56,7 @@ class NSEventMonitor {
}
}

class CGEventMonitor {
class CGEventMonitor: EventMonitor {
private var eventTap: CFMachPort?
private var runLoopSource: CFRunLoopSource?
private var eventCallback: (CGEvent) -> Unmanaged<CGEvent>?
Expand Down
19 changes: 19 additions & 0 deletions Loop/Window Management/Window.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SwiftUI
class Window {
let axWindow: AXUIElement
let cgWindowID: CGWindowID
let nsRunningApplication: NSRunningApplication?
var processID: pid_t

init?(element: AXUIElement, pid: pid_t? = nil) {
Expand All @@ -22,6 +23,10 @@ class Window {
self.processID = pid!
}

self.nsRunningApplication = NSWorkspace.shared.runningApplications.first(where: {
$0.processIdentifier == pid!
})

// Set self's CGWindowID
var windowId = CGWindowID(0)
let result = _AXUIElementGetWindow(self.axWindow, &windowId)
Expand Down Expand Up @@ -65,6 +70,20 @@ class Window {
return self.axWindow.setValue(.fullScreen, value: state)
}

var isHidden: Bool {
return self.nsRunningApplication?.isHidden ?? false
}
@discardableResult
func setHidden(_ state: Bool) -> Bool {
var result = false
if state {
result = self.nsRunningApplication?.hide() ?? false
} else {
result = self.nsRunningApplication?.unhide() ?? false
}
return result
}

var isMinimized: Bool {
let result = self.axWindow.getValue(.minimized) as? NSNumber
return result?.boolValue ?? false
Expand Down
Loading

0 comments on commit 9261020

Please sign in to comment.