blob: cf3bb51824748ba0aed2ab2cd2507dcb81b30ec4 [file] [log] [blame]
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -04001/*
2 * Copyright (C) 2019 Savoir-faire Linux Inc.
3 * Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#import "MovableView.h"
21#import <QuartzCore/QuartzCore.h>
22
23@implementation MovableView
24
25NSPoint firstMouseDownPoint = NSZeroPoint;
26NSInteger const MARGIN = 20;
27BOOL movingFromCorner;
28BOOL movingToCorner;
29
30@synthesize movable;
31
32-(void)mouseDown:(NSEvent *)event
33{
34 if (!movable) {
35 return;
36 }
37 firstMouseDownPoint = [self.hostingView convertPoint:event.locationInWindow toView:self];
38}
39
40-(void)mouseDragged:(NSEvent *)event
41{
42 if (!movable) {
43 return;
44 }
45 NSPoint newPoint = [self.hostingView convertPoint:event.locationInWindow toView:self];
46 NSPoint offset = CGPointMake(newPoint.x - firstMouseDownPoint.x,newPoint.y - firstMouseDownPoint.y);
47 NSPoint origin = self.frame.origin;
48 NSSize size = self.frame.size;
49 NSPoint newOrigin= CGPointMake(origin.x + offset.x, origin.y + offset.y);
50 NSPoint newMax= CGPointMake(newOrigin.x + self.frame.size.width, newOrigin.y + self.frame.size.height);
51 if(!CGRectContainsPoint(CGRectInset([self.hostingView frame], MARGIN, MARGIN), newOrigin)
52 || !CGRectContainsPoint(CGRectInset([self.hostingView frame], MARGIN, MARGIN), newMax)) {
53 if (newOrigin.x < self.minX) {
54 newOrigin.x = self.minX;
55 }
56 if (newOrigin.x > self.maxX) {
57 newOrigin.x = self.maxX;
58 }
59 if (newOrigin.y < self.minY) {
60 newOrigin.y = self.minY;
61 }
62 if (newOrigin.y > self.maxY) {
63 newOrigin.y = self.maxY;
64 }
65 }
66 self.frame = CGRectMake(newOrigin.x, newOrigin.y, size.width, size.height);
67}
68
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -040069- (void)mouseUp:(NSEvent *)event {
Kateryna Kostiuk2aa39232019-10-10 11:55:11 -040070 [self moveToCornerWithDuration:0.2];
Kateryna Kostiuk6891d4f2019-09-19 17:44:33 -040071}
72
73-(void) moveToCornerWithDuration:(CGFloat) duration {
74 if (!movable) {
75 return;
76 }
77 NSRect frame = self.frame;
78 CGPoint currentOrigin = frame.origin;
79 auto closestCorner = self.closestCorner;
80 frame.origin = [self pointForCorner: self.closestCorner];
81 [NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
82 context.duration = duration;
83 context.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
84 self.animator.frame = frame;
85 } completionHandler: ^{
86 movingToCorner = false;
87 }];
88}
89
90- (CGFloat) maxY {
91 return self.hostingView.frame.size.height - self.frame.size.height - MARGIN;
92}
93
94- (CGFloat) maxX {
95 return self.hostingView.frame.size.width - self.frame.size.width - MARGIN;
96}
97
98- (CGFloat) minX {
99 return MARGIN;
100}
101
102- (CGFloat) minY {
103 return MARGIN;
104}
105
106- (ViewCorner) closestCorner {
107 NSPoint origin = self.frame.origin;
108 BOOL isLeft = origin.x < self.maxX * 0.5;
109 BOOL isTop = origin.y > self.maxY * 0.5;
110 if (isLeft) {
111 if (isTop) {
112 return TOP_LEFT;
113 }
114 return BOTTOM_LEFT;
115 }
116 if (isTop) {
117 return TOP_RIGHT;
118 }
119 return BOTTOM_RIGHT;
120}
121
122- (CGPoint) pointForCorner:(NSInteger) corner {
123 switch (corner) {
124 case TOP_LEFT:
125 return CGPointMake(self.minX, self.maxY);
126 case BOTTOM_LEFT:
127 return CGPointMake(self.minX, self.minY);
128 case TOP_RIGHT: {
129 auto max = self.maxY;
130 return CGPointMake(self.maxX, self.maxY);
131 }
132 case BOTTOM_RIGHT:
133 return CGPointMake(self.maxX, self.minY);
134 }
135}
136
137- (CGPoint) pointToMoveFromCorner:(NSInteger) corner {
138 CGFloat margin = 4;
139 CGPoint currentOrigin = [self pointForCorner: corner];
140 switch (corner) {
141 case TOP_LEFT:
142 return CGPointMake(currentOrigin.x + margin, currentOrigin.y - margin);
143 case BOTTOM_LEFT:
144 return CGPointMake(currentOrigin.x + margin, currentOrigin.y + margin);
145 case TOP_RIGHT:
146 return CGPointMake(currentOrigin.x - margin, currentOrigin.y - margin);
147 case BOTTOM_RIGHT:
148 return CGPointMake(currentOrigin.x - margin, currentOrigin.y + margin);
149 }
150}
151
152@end