blob: cf3bb51824748ba0aed2ab2cd2507dcb81b30ec4 [file] [log] [blame]
/*
* Copyright (C) 2019 Savoir-faire Linux Inc.
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "MovableView.h"
#import <QuartzCore/QuartzCore.h>
@implementation MovableView
NSPoint firstMouseDownPoint = NSZeroPoint;
NSInteger const MARGIN = 20;
BOOL movingFromCorner;
BOOL movingToCorner;
@synthesize movable;
-(void)mouseDown:(NSEvent *)event
{
if (!movable) {
return;
}
firstMouseDownPoint = [self.hostingView convertPoint:event.locationInWindow toView:self];
}
-(void)mouseDragged:(NSEvent *)event
{
if (!movable) {
return;
}
NSPoint newPoint = [self.hostingView convertPoint:event.locationInWindow toView:self];
NSPoint offset = CGPointMake(newPoint.x - firstMouseDownPoint.x,newPoint.y - firstMouseDownPoint.y);
NSPoint origin = self.frame.origin;
NSSize size = self.frame.size;
NSPoint newOrigin= CGPointMake(origin.x + offset.x, origin.y + offset.y);
NSPoint newMax= CGPointMake(newOrigin.x + self.frame.size.width, newOrigin.y + self.frame.size.height);
if(!CGRectContainsPoint(CGRectInset([self.hostingView frame], MARGIN, MARGIN), newOrigin)
|| !CGRectContainsPoint(CGRectInset([self.hostingView frame], MARGIN, MARGIN), newMax)) {
if (newOrigin.x < self.minX) {
newOrigin.x = self.minX;
}
if (newOrigin.x > self.maxX) {
newOrigin.x = self.maxX;
}
if (newOrigin.y < self.minY) {
newOrigin.y = self.minY;
}
if (newOrigin.y > self.maxY) {
newOrigin.y = self.maxY;
}
}
self.frame = CGRectMake(newOrigin.x, newOrigin.y, size.width, size.height);
}
- (void)mouseUp:(NSEvent *)event {
[self moveToCornerWithDuration:0.2];
}
-(void) moveToCornerWithDuration:(CGFloat) duration {
if (!movable) {
return;
}
NSRect frame = self.frame;
CGPoint currentOrigin = frame.origin;
auto closestCorner = self.closestCorner;
frame.origin = [self pointForCorner: self.closestCorner];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
context.duration = duration;
context.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
self.animator.frame = frame;
} completionHandler: ^{
movingToCorner = false;
}];
}
- (CGFloat) maxY {
return self.hostingView.frame.size.height - self.frame.size.height - MARGIN;
}
- (CGFloat) maxX {
return self.hostingView.frame.size.width - self.frame.size.width - MARGIN;
}
- (CGFloat) minX {
return MARGIN;
}
- (CGFloat) minY {
return MARGIN;
}
- (ViewCorner) closestCorner {
NSPoint origin = self.frame.origin;
BOOL isLeft = origin.x < self.maxX * 0.5;
BOOL isTop = origin.y > self.maxY * 0.5;
if (isLeft) {
if (isTop) {
return TOP_LEFT;
}
return BOTTOM_LEFT;
}
if (isTop) {
return TOP_RIGHT;
}
return BOTTOM_RIGHT;
}
- (CGPoint) pointForCorner:(NSInteger) corner {
switch (corner) {
case TOP_LEFT:
return CGPointMake(self.minX, self.maxY);
case BOTTOM_LEFT:
return CGPointMake(self.minX, self.minY);
case TOP_RIGHT: {
auto max = self.maxY;
return CGPointMake(self.maxX, self.maxY);
}
case BOTTOM_RIGHT:
return CGPointMake(self.maxX, self.minY);
}
}
- (CGPoint) pointToMoveFromCorner:(NSInteger) corner {
CGFloat margin = 4;
CGPoint currentOrigin = [self pointForCorner: corner];
switch (corner) {
case TOP_LEFT:
return CGPointMake(currentOrigin.x + margin, currentOrigin.y - margin);
case BOTTOM_LEFT:
return CGPointMake(currentOrigin.x + margin, currentOrigin.y + margin);
case TOP_RIGHT:
return CGPointMake(currentOrigin.x - margin, currentOrigin.y - margin);
case BOTTOM_RIGHT:
return CGPointMake(currentOrigin.x - margin, currentOrigin.y + margin);
}
}
@end