blob: 96d3f550287ccd6da68c1dd2e6eb2f3c3725fc36 [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) {
129 return;
130 }
131 CGFloat viewRatio = viewSize.width / viewSize.height;
132 CGFloat frameRatio = self.framesize.width / self.framesize.height;
133 CGFloat ratio = viewRatio * (1/frameRatio);
134 // calculate size for all participants
135 CGFloat allViewsWidth = viewSize.width;
136 CGFloat allViewsHeight = viewSize.height;
137 if (ratio < 1) {
138 allViewsHeight = allViewsHeight * ratio;
139 } else {
140 allViewsWidth = allViewsWidth / ratio;
141 }
142 CGFloat widthRatio = self.participant.width / self.framesize.width;
143 CGFloat heightRatio = self.participant.hight / self.framesize.height;
144
145 CGFloat overlayWidth = allViewsWidth * widthRatio;
146 CGFloat overlayHeight = allViewsHeight * heightRatio;
147 CGFloat ratioX = overlayWidth / viewSize.width;
148 CGFloat ratioY = overlayHeight / viewSize.height;
149 CGFloat offsetx = (viewSize.width - allViewsWidth) * 0.5;
150 CGFloat offsety = (viewSize.height - allViewsHeight) * 0.5;
151 CGFloat centerX = (offsetx + (self.participant.x * (overlayWidth / self.participant.width))+ overlayWidth * 0.5) / (viewSize.width * 0.5);
152 CGFloat centerY = (offsety + (self.participant.y * (overlayHeight / self.participant.hight)) + overlayHeight * 0.5) / (viewSize.height * 0.5);
153
154 self.centerXConstraint = [NSLayoutConstraint constraintWithItem:self
155 attribute:NSLayoutAttributeCenterX
156 relatedBy:NSLayoutRelationEqual toItem:self.superview
157 attribute:NSLayoutAttributeCenterX
158 multiplier:centerX constant:0];
159 self.centerYConstraint = [NSLayoutConstraint constraintWithItem:self
160 attribute:NSLayoutAttributeCenterY
161 relatedBy:NSLayoutRelationEqual toItem:self.superview
162 attribute:NSLayoutAttributeCenterY
163 multiplier:centerY constant:0];
164 self.widthConstraint =
165 [self.widthAnchor constraintEqualToAnchor:self.superview.widthAnchor multiplier: ratioX];
166 self.heightConstraint = [self.heightAnchor constraintEqualToAnchor:self.superview.heightAnchor multiplier: ratioY];
167 self.widthConstraint.active = YES;
168 self.heightConstraint.active = YES;
169 self.centerXConstraint.active = YES;
170 self.centerYConstraint.active = YES;
171 [self layoutSubtreeIfNeeded];
172 CGFloat width = self.frame.size.width;
173 self.usernameLabel.hidden = width < 150;
174}
175
176- (void)updateViewWithParticipant:(ConferenceParticipant) participant {
177 self.participant = participant;
178 [self sizeChanged];
179 self.usernameLabel.string = self.participant.bestName;
180}
181
182-(void)mouseEntered:(NSEvent *)theEvent {
183 self.alphaValue = 1;
184 [super mouseEntered:theEvent];
185}
186
187-(void)mouseExited:(NSEvent *)theEvent {
188 self.alphaValue = 0;
189 [super mouseExited:theEvent];
190}
191
192- (void)ensureTrackingArea {
193 if (trackingArea == nil) {
194 trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
195 options:NSTrackingInVisibleRect
196 | NSTrackingActiveAlways
197 | NSTrackingMouseEnteredAndExited owner:self userInfo:nil];
198 }
199}
200
201- (void)updateTrackingAreas {
202 [super updateTrackingAreas];
203 [self ensureTrackingArea];
204 if (![[self trackingAreas] containsObject:trackingArea]) {
205 [self addTrackingArea:trackingArea];
206 }
207}
208
209
210@end