问题
I'm using a textured window that has a tab bar along the top of it, just below the title bar.
I've used -setContentBorderThickness:forEdge: on the window to make the gradient look right, and to make sure sheets slide out from the right position.
What's not working however, is dragging the window around. It works if I click and drag the area that's actually the title bar, but since the title bar gradient spills into a (potentially/often empty) tab bar, it's really easy to click too low and it feels really frustrating when you try to drag and realise the window is not moving.
I notice NSToolbar
, while occupying roughly the same amount of space below the title bar, allows the window to be dragged around when the cursor is over it. How does one implement this?
Thanks.
回答1:
I tried the mouseDownCanMoveWindow
solution (https://stackoverflow.com/a/4564146/901641) but it didn't work for me. I got rid of that method and instead added this to my window subclass:
- (BOOL)isMovableByWindowBackground {
return YES;
}
which worked like a charm.
回答2:
I found this here:
-(void)mouseDown:(NSEvent *)theEvent {
NSRect windowFrame = [[self window] frame];
initialLocation = [NSEvent mouseLocation];
initialLocation.x -= windowFrame.origin.x;
initialLocation.y -= windowFrame.origin.y;
}
- (void)mouseDragged:(NSEvent *)theEvent {
NSPoint currentLocation;
NSPoint newOrigin;
NSRect screenFrame = [[NSScreen mainScreen] frame];
NSRect windowFrame = [self frame];
currentLocation = [NSEvent mouseLocation];
newOrigin.x = currentLocation.x - initialLocation.x;
newOrigin.y = currentLocation.y - initialLocation.y;
// Don't let window get dragged up under the menu bar
if( (newOrigin.y+windowFrame.size.height) > (screenFrame.origin.y+screenFrame.size.height) ){
newOrigin.y=screenFrame.origin.y + (screenFrame.size.height-windowFrame.size.height);
}
//go ahead and move the window to the new location
[[self window] setFrameOrigin:newOrigin];
}
It works fine, though I'm not 100% sure I'm doing it correctly. There's one bug I've found so far, and that's if the drag begins inside a subview (a tab itself) and then enters the superview (the tab bar). The window jumps around. Some -hitTest: magic, or possibly even just invalidating initialLocation on mouseUp should probably fix that.
回答3:
Have you tried overriding the NSView method mouseDownCanMoveWindow to return YES
?
回答4:
It works for me after TWO steps:
- Subclass NSView, override the
mouseDownCanMoveWindow
to return YES. - Subclass NSWindow, override the
isMovableByWindowBackground
to return YES.
回答5:
As of macOS 10.11, the simplest way to do this is to utilize the new -[NSWindow performWindowDragWithEvent:]
method:
@interface MyView () {
BOOL movingWindow;
}
@end
@implementation MyView
...
- (BOOL)mouseDownCanMoveWindow
{
return NO;
}
- (void)mouseDown:(NSEvent *)event
{
movingWindow = NO;
CGPoint point = [self convertPoint:event.locationInWindow
fromView:nil];
// The area in your view where you want the window to move:
CGRect movableRect = CGRectMake(0, 0, 100, 100);
if (self.window.movableByWindowBackground &&
CGRectContainsPoint(movableRect, point)) {
[self.window performWindowDragWithEvent:event];
movingWindow = YES;
return;
}
// Handle the -mouseDown: as usual
}
- (void)mouseDragged:(NSEvent *)event
{
if (movingWindow) return;
// Handle the -mouseDragged: as usual
}
@end
Here, -performWindowDragWithEvent:
will handle the correct behavior of not overlapping the menu bar, and will also snap to edges on macOS 10.12 and later. Be sure to include a BOOL movingWindow
instance variable with your view's private interface so you can avoid -mouseDragged:
events once you determined you don't want to process them.
Here, we are also checking that -[NSWindow movableByWindowBackground]
is set to YES
so that this view can be used in non-movable-by-window-background windows, but that is optional.
回答6:
It's quite easy:
override mouseDownCanMoveWindow property
override var mouseDownCanMoveWindow:Bool {
return false
}
回答7:
If you got a NSTableView
in your window, with selection enabled, overriding the mouseDownCanMoveWindow property won't work.
You need instead to create a NSTableView
subclass and override the following mouse events (and use the performWindowDragWithEvent: mentioned in Dimitri answer):
@interface WindowDraggableTableView : NSTableView
@end
@implementation WindowDraggableTableView
{
BOOL _draggingWindow;
NSEvent *_mouseDownEvent;
}
- (void)mouseDown:(NSEvent *)event
{
if (self.window.movableByWindowBackground == NO) {
[super mouseDown:event]; // Normal behavior.
return;
}
_draggingWindow = NO;
_mouseDownEvent = event;
}
- (void)mouseDragged:(NSEvent *)event
{
if (self.window.movableByWindowBackground == NO) {
[super mouseDragged:event]; // Normal behavior.
return;
}
assert(_mouseDownEvent);
_draggingWindow = YES;
[self.window performWindowDragWithEvent:_mouseDownEvent];
}
- (void)mouseUp:(NSEvent *)event
{
if (self.window.movableByWindowBackground == NO) {
[super mouseUp:event]; // Normal behavior.
return;
}
if (_draggingWindow == YES) {
_draggingWindow = NO;
return; // Event already handled by `performWindowDragWithEvent`.
}
// Triggers regular table selection.
NSPoint locationInWindow = event.locationInWindow;
NSPoint locationInTable = [self convertPoint:locationInWindow fromView:nil];
NSInteger row = [self rowAtPoint:locationInTable];
if (row >= 0 && [self.delegate tableView:self shouldSelectRow:row])
{
NSIndexSet *rowIndex = [NSIndexSet indexSetWithIndex:row];
[self selectRowIndexes:rowIndex byExtendingSelection:NO];
}
}
@end
Also don't forget to set the corresponding window movableByWindowBackground property as well:
self.window.movableByWindowBackground = YES;
回答8:
When you set property isMovableByWindowBackground
in viewDidLoad
, it may not work because the window
property of the view
is not yet set. In that case, try this:
override func viewDidAppear() {
self.view.window?.isMovableByWindowBackground = true
}
来源:https://stackoverflow.com/questions/4563893/allow-click-and-dragging-a-view-to-drag-the-window-itself