I've been trying to solve this very frustrating issue and I'm hoping someone can help. I am trying to create an NSWindow which has multiple views. The views can overlap each other. These are the requirements.
- The blue NSView as seen in the image below is what can move the window.
- The yellow NSView cannot move the window. This is another NSView instance which is underneath the blue NSView.
- The movement needs to feel like any other window movement.
- Things like dragging the window all the way to the top should activate Expose (Sierra and High Sierra).
First Attempt
I have attempted this by trying the following. Unfortunately this simply doesn't work.
In NSWindow
override func awakeFromNib() {
self.refreshElements()
self.titlebarAppearsTransparent = true
self.isMovableByWindowBackground = true
}
In my custom views
class Blue: NSView {
public override var mouseDownCanMoveWindow: Bool { return true }
}
class Yellow: NSView {
public override var mouseDownCanMoveWindow: Bool { return false }
}
Second Attempt
Then I have tried implementing mouseDown
mouseDragged
and mouseUp
in Blue. I would calculate the mouse movement and self.setFrame(frameRect:display:animate:)
on the window. This worked OK in that I could control moving the window but it had a couple of issues. One no matter what the window movement is always slower. I think it's animating even when I set the animate
property to false. Also it does not active Expose as other windows do.
Third Attempt
This is as close as I can get to the behavior I want with one major issue. The initial mouseDown does not register. So you can't click on the blue view and drag and move the window. You have mouseDown, mouseUp, then mouseDown again in order for dragging to move the window.
public class MyWindow: NSWindow {
@IBOutlet var blue: NSView!
@IBOutlet var yellow: NSView!
public override func awakeFromNib() {
self.titlebarAppearsTransparent = true
self.isMovable = false
self.isMovableByWindowBackground = true
}
public override func mouseDown(with event: NSEvent) {
let point = event.locationInWindow
let move = NSPointInRect(point, self.blue.frame)
self.isMovableByWindowBackground = move
self.isMovable = move
super.mouseDown(with: event)
}
public override func mouseDragged(with event: NSEvent) {
super.mouseDragged(with: event)
}
}
My preferred method is method 3. It is the cleanest code and outside of the unusual behavior of having to click then click again in order for things to work everything else is perfect. My question is how can I force the initial click to also register with the window after it is dynamically set to allow movement?