blob: 6a2c086dd199d6b3a99c18e49132366772cb3a73 [file] [log] [blame]
Kateryna Kostiuk1705a5a2020-08-26 09:48:00 -04001/*
2* Copyright (C) 2020 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 "ConferenceOverlayView.h"
21
22@implementation ConferenceOverlayView
23@synthesize contextualMenu;
24
25CGFloat const margin = 6;
26CGFloat const controlSize = 40;
27
28- (instancetype)initWithFrame:(NSRect)frame
29{
30 self = [super initWithFrame:frame];
31 if (self) {
32 self.translatesAutoresizingMaskIntoConstraints = false;
33 [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable | NSViewMinYMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMaxXMargin];
34 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sizeChanged) name:NSWindowDidResizeNotification object:nil];
35 self.alphaValue = 0;
36 }
37 return self;
38}
39
40- (void)configureView {
41 self.gradientView = [[GradientView alloc] init];
42 self.gradientView.startingColor = [NSColor clearColor];
43 self.gradientView.endingColor = [NSColor blackColor];
44 self.gradientView.angle = 270;
45 self.gradientView.translatesAutoresizingMaskIntoConstraints = false;
46 [self addSubview: self.gradientView];
47 [self.gradientView.widthAnchor constraintEqualToAnchor:self.widthAnchor multiplier: 1].active = TRUE;
48 [self.gradientView.heightAnchor constraintEqualToConstant: controlSize].active = true;
49 [self.gradientView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor].active = true;
50 [self.gradientView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = true;
51 if ([self.delegate isMasterCall]) {
52 self.settingsButton = [[IconButton alloc] init];
53 self.settingsButton.transparent = true;
54 self.settingsButton.title = @"";
55 NSImage* settingsImage = [NSImage imageNamed: @"ic_more.png"];
56 [self.settingsButton setImage:settingsImage];
57 self.settingsButton.bgColor = [NSColor clearColor];
58 self.settingsButton.imageColor = [NSColor whiteColor];
59 self.settingsButton.imagePressedColor = [NSColor lightGrayColor];
60 self.settingsButton.imageInsets = margin;
61 self.settingsButton.translatesAutoresizingMaskIntoConstraints = false;
62 [self.gradientView addSubview:self.settingsButton];
63
64 [self.settingsButton.widthAnchor constraintEqualToConstant: controlSize].active = TRUE;
65 [self.settingsButton.heightAnchor constraintEqualToConstant: controlSize].active = true;
66 [self.settingsButton.trailingAnchor constraintEqualToAnchor: self.gradientView.trailingAnchor].active = true;
67 [self.settingsButton.bottomAnchor constraintEqualToAnchor:self.gradientView.bottomAnchor].active = true;
68 [self.settingsButton setAction:@selector(triggerMenu:)];
69 [self.settingsButton setTarget:self];
70 }
71 self.usernameLabel = [[NSTextView alloc] init];
72 self.usernameLabel.textColor = [NSColor whiteColor];
73 self.usernameLabel.editable = NO;
74 self.usernameLabel.drawsBackground = NO;
75 self.usernameLabel.backgroundColor = [NSColor clearColor];
76 self.usernameLabel.font = [NSFont userFontOfSize: 14.0];
77 self.usernameLabel.translatesAutoresizingMaskIntoConstraints = false;
78 self.usernameLabel.textContainer.maximumNumberOfLines = 1;
79 [self.gradientView addSubview:self.usernameLabel];
80
81 [self.usernameLabel.leadingAnchor constraintEqualToAnchor: self.gradientView.leadingAnchor constant: margin * 2].active = true;
82 [self.usernameLabel.trailingAnchor constraintEqualToAnchor: self.gradientView.trailingAnchor constant: -(controlSize + margin)].active = true;
83 [self.usernameLabel.bottomAnchor constraintEqualToAnchor:self.gradientView.bottomAnchor constant: - margin * 2].active = true;
84}
85
86- (IBAction)triggerMenu:(id)sender {
87 int layout = [self.delegate getCurrentLayout];
88 if (layout < 0)
89 return;
90 BOOL showMaximized = layout != 2;
91 BOOL showMinimized = !(layout == 0 || (layout == 1 && !self.participant.active));
92 contextualMenu = [[NSMenu alloc] initWithTitle:@""];
93 if (showMinimized) {
94 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Minimize participant", @"Conference action") action:@selector(minimize:) keyEquivalent:@""];
95 [menuItem setTarget:self];
96 [contextualMenu insertItem:menuItem atIndex:contextualMenu.itemArray.count];
97 }
98 if (showMaximized) {
99 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Maximize participant", @"Conference action") action:@selector(maximize:) keyEquivalent:@""];
100 [menuItem setTarget:self];
101 [contextualMenu insertItem:menuItem atIndex:contextualMenu.itemArray.count];
102 }
103 if (!self.participant.isLocal) {
104 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Hangup", @"Conference action") action:@selector(finishCall:) keyEquivalent:@""];
105 [menuItem setTarget:self];
106 [contextualMenu insertItem:menuItem atIndex:contextualMenu.itemArray.count];
107 }
108 [contextualMenu popUpMenuPositioningItem:nil atLocation:[NSEvent mouseLocation] inView:nil];
109}
110
111- (void)minimize:(NSMenuItem*) sender {
112 [self.delegate minimizeParticipant];
113}
114
115- (void)maximize:(NSMenuItem*) sender {
116 [self.delegate maximizeParticipant:self.participant.uri active: self.participant.active];
117}
118
119- (void)finishCall:(NSMenuItem*) sender {
120 [self.delegate hangUpParticipant:self.participant.uri];
121}
122
123- (void)sizeChanged {
124 NSArray* constraints = [NSArray arrayWithObjects: self.widthConstraint, self.heightConstraint, self.centerXConstraint, self.centerYConstraint, nil];
125 [self.superview removeConstraints: constraints];
126 [self.superview layoutSubtreeIfNeeded];
127 CGSize viewSize = self.superview.frame.size;
128 if (viewSize.width == 0 || viewSize.height == 0 || self.framesize.width == 0 || self.framesize.height == 0 || self.participant.width == 0 || self.participant.hight == 0) {
Kateryna Kostiuka9908a32020-09-10 10:10:58 -0400129 self.frame = CGRectZero;
Kateryna Kostiuk1705a5a2020-08-26 09:48:00 -0400130 return;
131 }
132 CGFloat viewRatio = viewSize.width / viewSize.height;
133 CGFloat frameRatio = self.framesize.width / self.framesize.height;
134 CGFloat ratio = viewRatio * (1/frameRatio);
135 // calculate size for all participants
136 CGFloat allViewsWidth = viewSize.width;
137 CGFloat allViewsHeight = viewSize.height;
138 if (ratio < 1) {
139 allViewsHeight = allViewsHeight * ratio;
140 } else {
141 allViewsWidth = allViewsWidth / ratio;
142 }
143 CGFloat widthRatio = self.participant.width / self.framesize.width;
144 CGFloat heightRatio = self.participant.hight / self.framesize.height;
145
146 CGFloat overlayWidth = allViewsWidth * widthRatio;
147 CGFloat overlayHeight = allViewsHeight * heightRatio;
148 CGFloat ratioX = overlayWidth / viewSize.width;
149 CGFloat ratioY = overlayHeight / viewSize.height;
150 CGFloat offsetx = (viewSize.width - allViewsWidth) * 0.5;
151 CGFloat offsety = (viewSize.height - allViewsHeight) * 0.5;
152 CGFloat centerX = (offsetx + (self.participant.x * (overlayWidth / self.participant.width))+ overlayWidth * 0.5) / (viewSize.width * 0.5);
153 CGFloat centerY = (offsety + (self.participant.y * (overlayHeight / self.participant.hight)) + overlayHeight * 0.5) / (viewSize.height * 0.5);
154
155 self.centerXConstraint = [NSLayoutConstraint constraintWithItem:self
156 attribute:NSLayoutAttributeCenterX
157 relatedBy:NSLayoutRelationEqual toItem:self.superview
158 attribute:NSLayoutAttributeCenterX
159 multiplier:centerX constant:0];
160 self.centerYConstraint = [NSLayoutConstraint constraintWithItem:self
161 attribute:NSLayoutAttributeCenterY
162 relatedBy:NSLayoutRelationEqual toItem:self.superview
163 attribute:NSLayoutAttributeCenterY
164 multiplier:centerY constant:0];
165 self.widthConstraint =
166 [self.widthAnchor constraintEqualToAnchor:self.superview.widthAnchor multiplier: ratioX];
167 self.heightConstraint = [self.heightAnchor constraintEqualToAnchor:self.superview.heightAnchor multiplier: ratioY];
168 self.widthConstraint.active = YES;
169 self.heightConstraint.active = YES;
170 self.centerXConstraint.active = YES;
171 self.centerYConstraint.active = YES;
172 [self layoutSubtreeIfNeeded];
173 CGFloat width = self.frame.size.width;
174 self.usernameLabel.hidden = width < 150;
175}
176
177- (void)updateViewWithParticipant:(ConferenceParticipant) participant {
178 self.participant = participant;
179 [self sizeChanged];
180 self.usernameLabel.string = self.participant.bestName;
181}
182
183-(void)mouseEntered:(NSEvent *)theEvent {
184 self.alphaValue = 1;
185 [super mouseEntered:theEvent];
186}
187
188-(void)mouseExited:(NSEvent *)theEvent {
189 self.alphaValue = 0;
190 [super mouseExited:theEvent];
191}
192
Kateryna Kostiukcd027882020-09-08 10:59:31 -0400193-(void)mouseUp:(NSEvent *)theEvent
194{
195 [super mouseUp:theEvent];
196 if ([theEvent clickCount] == 1) {
197 [self performSelector:@selector(singleTap) withObject:nil afterDelay:[NSEvent doubleClickInterval]];
198 } else if (theEvent.clickCount == 2) {
199 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTap) object:nil];
200 }
201}
202
203- (void)singleTap {
204 [self.delegate maximizeParticipant:self.participant.uri active: self.participant.active];
205}
206
Kateryna Kostiuk1705a5a2020-08-26 09:48:00 -0400207- (void)ensureTrackingArea {
208 if (trackingArea == nil) {
209 trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
210 options:NSTrackingInVisibleRect
211 | NSTrackingActiveAlways
212 | NSTrackingMouseEnteredAndExited owner:self userInfo:nil];
213 }
214}
215
216- (void)updateTrackingAreas {
217 [super updateTrackingAreas];
218 [self ensureTrackingArea];
219 if (![[self trackingAreas] containsObject:trackingArea]) {
220 [self addTrackingArea:trackingArea];
221 }
222}
223
224
225@end