CutBox.app

Coverage Report

Created: 2024-03-12 03:40

.../Source/Components/Popup/PopupController.swift
Line
Count
Source (jump to first uncovered line)
1
//
2
//  PopupController.swift
3
//  CutBox
4
//
5
//  Created by Jason Milkins on 17/3/18.
6
//  Copyright © 2018-2023 ocodo. All rights reserved.
7
//
8
9
import Cocoa
10
11
class PopupController: CutBoxBaseWindowController {
12
13
179
    let panel = PopupPanel()
14
15
179
    let backgroundView = PopupBackgroundView()
16
179
    let containerView = PopupContainerView()
17
    var contentView: CutBoxBaseView
18
19
    var currentHeight: Double = 0
20
    var currentWidth: Double = 0
21
22
179
    var proportionalWidth: Double = 1.0 / 1.6
23
179
    var proportionalHeight: Double = 1.0 / 1.8
24
    var proportionalTopPadding: CGFloat = 0
25
26
    var contentInset: CGFloat {
27
11
        get {
28
11
            return containerView.contentInset
29
11
        }
30
31
1
        set {
32
1
            let size = containerView.frame.size
33
1
            containerView.contentInset = newValue
34
1
            resizePopup(width: Double(size.width),
35
1
                        height: Double(size.height))
36
1
        }
37
    }
38
39
    var isOpen: Bool = false
40
41
    var isOpening: Bool = false
42
43
    var willOpenPopup: (() -> Void)?
44
    var didOpenPopup: (() -> Void)?
45
    var willClosePopup: (() -> Void)?
46
    var didClosePopup: (() -> Void)?
47
48
    var lastMouseDownEvent: NSEvent?
49
    var mouseDownEventMonitor: Any?
50
    var mouseUpEventMonitor: Any?
51
52
    var lastKeyDownEvent: NSEvent?
53
    var keyDownEventMonitor: Any?
54
55
178
    init(content: CutBoxBaseView) {
56
178
        self.contentView = content
57
178
        super.init(window: panel)
58
178
        setup()
59
178
    }
60
61
1
    required init?(coder: NSCoder) {
62
1
        self.contentView = coder.decodeObject(forKey: "contentView") as? CutBoxBaseView ?? CutBoxBaseView()
63
1
        super.init(coder: coder)
64
1
        self.window = panel
65
1
        setup()
66
1
    }
67
68
179
    func setup() {
69
179
        panel.windowController = self
70
179
        panel.acceptsMouseMovedEvents = true
71
179
72
179
        panel.level = NSWindow.Level(rawValue:
73
179
            Int(CGWindowLevelForKey(CGWindowLevelKey.popUpMenuWindow))
74
179
        )
75
179
76
179
        panel.isOpaque = false
77
179
        panel.backgroundColor = NSColor.clear
78
179
        panel.styleMask = .nonactivatingPanel
79
179
        panel.hidesOnDeactivate = false
80
179
        panel.hasShadow = true
81
179
        panel.delegate = self
82
179
        panel.contentView = backgroundView
83
179
        backgroundView.addSubview(containerView)
84
179
85
179
        containerView.setup()
86
179
        containerView.contentView = contentView
87
179
        panel.initialFirstResponder = contentView
88
179
    }
89
90
3
    func openPopup() {
91
3
        openPanel()
92
3
    }
93
94
8
    func closePopup() {
95
8
        closePanel()
96
8
    }
97
98
2
    @objc func togglePopup() {
99
2
        if isOpen {
100
1
            closePopup()
101
2
        } else {
102
1
            openPopup()
103
2
        }
104
2
    }
105
106
5
    func resizePopup(width: Double, height: Double) {
107
5
        guard let screen = NSScreen.currentScreenForMouseLocation()
108
5
            else { return }
109
5
        var frame = panel.frame
110
5
        var newSize = CGSize(width: width,
111
5
                             height: height)
112
5
113
5
        newSize.height += contentInset * 2
114
5
        newSize.width += contentInset * 2
115
5
116
5
        frame.origin.y = (screen.frame.height - CGFloat(height))
117
5
118
5
        frame.size.height = newSize.height
119
5
120
5
        let widthDifference = newSize.width - frame.size.width
121
5
        if widthDifference != 0 {
122
5
            frame.size.width = newSize.width
123
5
        }
124
5
125
5
        currentWidth = width
126
5
        currentHeight = height
127
5
128
5
        containerView.resetConstraints()
129
5
        panel.setFrame(frame, display: true, animate: panel.isVisible)
130
5
    }
131
132
1
    func proportionalResizePopup() {
133
1
        guard let screen = NSScreen.currentScreenForMouseLocation()
134
1
            else { return }
135
1
136
1
        var proportionalWidth = self.proportionalWidth
137
1
138
1
        if screen.frame.width < screen.frame.height {
139
0
            proportionalWidth += 0.25
140
1
        }
141
1
142
1
        let width = Double(screen.frame.width) * proportionalWidth
143
1
        let height = Double(screen.frame.height) * self.proportionalHeight
144
1
145
1
        currentWidth = width
146
1
        currentHeight = height
147
1
148
1
        resizePopup(width: width, height: height)
149
1
    }
150
151
1
    func resizePopup(width: Double) {
152
1
        if width != currentWidth {
153
1
            resizePopup(
154
1
                width: width,
155
1
                height: Double(contentView.frame.size.height))
156
1
        }
157
1
    }
158
159
1
    func resizePopup(height: Double) {
160
1
        if height != currentHeight {
161
1
            resizePopup(
162
1
                width: Double(contentView.frame.size.width),
163
1
                height: height)
164
1
        }
165
1
    }
166
167
3
    private func openPanel() {
168
3
        self.isOpening = true
169
3
        willOpenPopup?()
170
3
        self.isOpen = true
171
3
        self.contentView.isHidden = false
172
3
        let panelRect = rect(forPanel: self.panel)
173
3
        self.panel.setFrame(panelRect, display: true)
174
3
175
3
        NSApp.activate(ignoringOtherApps: false)
176
3
177
3
        self.panel.makeKeyAndOrderFront(self)
178
3
        self.panel.alphaValue = 1
179
3
        self.isOpening = false
180
3
        if self.isOpen {
181
3
            self.didOpenPopup?()
182
3
            self.panel.makeKeyAndOrderFront(self)
183
3
        }
184
3
    }
185
186
8
    private func closePanel() {
187
8
        willClosePopup?()
188
8
        self.isOpen = false
189
8
        self.contentView.isHidden = true
190
8
        self.panel.alphaValue = 0
191
8
        if !self.isOpen {
192
8
            self.panel.orderOut(nil)
193
8
            self.didClosePopup?()
194
8
        }
195
8
    }
196
197
3
    private func rect(forPanel panel: NSPanel) -> CGRect {
198
3
        guard let screen = NSScreen.currentScreenForMouseLocation()
199
3
            else { return CGRect.zero }
200
3
201
3
        let screenRect = screen.frame
202
3
        var panelRect = panel.frame
203
3
204
3
        panelRect.origin.y = screenRect.maxY - panelRect.height
205
3
        panelRect.origin.x = round(screenRect.midX - panelRect.width / 2)
206
3
207
3
        if panelRect.maxX > screenRect.maxX {
208
3
            panelRect.origin.x -= panelRect.maxX - screenRect.maxX
209
3
        }
210
3
211
3
        if panelRect.minX < screenRect.minX {
212
3
            panelRect.origin.x -= screenRect.minX - panelRect.minX
213
3
        }
214
3
215
3
        if panelRect.maxY > screenRect.maxY {
216
0
            panelRect.origin.y -= panelRect.maxY - screenRect.maxY
217
3
        }
218
3
219
3
        if panelRect.minY < screenRect.minY {
220
3
            panelRect.origin.y -= screenRect.minY - panelRect.minY
221
3
        }
222
3
223
3
        var proportionalTopPadding = self.proportionalTopPadding
224
3
225
3
        if screenRect.width < screenRect.height {
226
0
            proportionalTopPadding -= 0.09
227
3
        }
228
3
229
3
        let padding = screenRect.height * proportionalTopPadding
230
3
231
3
        panelRect.origin.y -= padding
232
3
233
3
        return panelRect
234
3
    }
235
}