From 3a2c9f7367d4f1a0be416da7d0baab96c5e33a44 Mon Sep 17 00:00:00 2001
From: kkostiuk <kateryna.kostiuk@savoirfairelinux.com>
Date: Wed, 27 Jan 2021 10:30:49 -0500
Subject: [PATCH] UI: change conference overlay for participant

Gitlab: #270

Change-Id: Ib460410f0df98640a835e37abcddfed85a34d311
---
 CMakeLists.txt                            |  14 +-
 data/light/ic_moderator.png               | Bin 0 -> 401 bytes
 data/light/ic_moderator_audio_muted.png   | Bin 0 -> 638 bytes
 data/light/ic_moderator_audio_unmuted.png | Bin 0 -> 541 bytes
 data/light/ic_moderator_hangup.png        | Bin 0 -> 513 bytes
 data/light/ic_moderator_maximize.png      | Bin 0 -> 407 bytes
 data/light/ic_moderator_minimize.png      | Bin 0 -> 427 bytes
 data/light/ic_star.png                    | Bin 0 -> 911 bytes
 src/CurrentCallVC.mm                      |  14 +-
 src/views/ConferenceOverlayView.h         |  23 +-
 src/views/ConferenceOverlayView.mm        | 279 +++++++++++++++-------
 src/views/CustomBackgroundView.h          |  39 +++
 src/views/CustomBackgroundView.mm         |  98 ++++++++
 13 files changed, 361 insertions(+), 106 deletions(-)
 create mode 100644 data/light/ic_moderator.png
 create mode 100644 data/light/ic_moderator_audio_muted.png
 create mode 100644 data/light/ic_moderator_audio_unmuted.png
 create mode 100644 data/light/ic_moderator_hangup.png
 create mode 100644 data/light/ic_moderator_maximize.png
 create mode 100644 data/light/ic_moderator_minimize.png
 create mode 100644 data/light/ic_star.png
 create mode 100644 src/views/CustomBackgroundView.h
 create mode 100644 src/views/CustomBackgroundView.mm

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0f381b0..9c7547e9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,11 +62,12 @@ add_custom_command (OUTPUT ${CMAKE_SOURCE_DIR}/Shader.metallib
                     COMMAND ${CMAKE_SOURCE_DIR}/generateShaderLib.sh
                     COMMENT "Creating Shader.metallib")
 
-                    add_custom_target(
+                  add_custom_target(
     shader ALL
     DEPENDS ${CMAKE_SOURCE_DIR}/Shader.metallib
     )
 
+
 IF(NOT (${ENABLE_SPARKLE} MATCHES false))
    MESSAGE("Sparkle auto-update enabled")
 
@@ -239,6 +240,8 @@ SET(ringclient_VIEWS
    src/views/VideoRendering.h
    src/views/ConferenceOverlayView.h
    src/views/ConferenceOverlayView.mm
+   src/views/CustomBackgroundView.h
+   src/views/CustomBackgroundView.mm
 )
 
 SET(ringclient_OTHERS
@@ -357,7 +360,14 @@ ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_record_stop.png
 ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_camera.png
 ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_audio_msg.png
 ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_group.png
-${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_picture.png)
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_picture.png
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator.png
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator_audio_muted.png
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator_audio_unmuted.png
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator_maximize.png
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator_minimize.png
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_moderator_hangup.png
+${CMAKE_CURRENT_SOURCE_DIR}/data/light/ic_star.png)
 
 SET_SOURCE_FILES_PROPERTIES(${ring_ICONS} PROPERTIES
        MACOSX_PACKAGE_LOCATION Resources)
diff --git a/data/light/ic_moderator.png b/data/light/ic_moderator.png
new file mode 100644
index 0000000000000000000000000000000000000000..039a4dcd3824a52ab303ee28843c539c99aeb8a7
GIT binary patch
literal 401
zcmV;C0dD?@P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px$OG!jQR7ef&l08d8Q5eTP)lee#CK`SM2ZJ_28X{U-oLpLT@(cJ08r@w)Cx^zC
zprKjl10*yAK~O@WlLTtl-;?LWxtFWAu%_;TAJ2LI&l~5tHxdcY|Ji`!I4wvodNJ`s
zC<Wxi$!Wl*D-WRueK>Okw1b|2Cm2F@PR<FjN077S%0Z9;w=jlF*fR;-6<onR+`t_~
zt#}N-4stp(<3FHGFb7?Dg`zaUR^c5EAO$bb)UhM(5AY19AZwK4`+~AAS|_0f6S#&E
zR3s*cyR1TD@~RJL!;X#x5BTgdYm}!@)^cGqW6{n*)><%{Sp4q~&;?P7Vs;a)ucGhz
z=&k{4-UP3&lp1<9@zE!kH@}#zt=P_A+`|EZTlIeA41dOPVAbM;eLJj-L*pLzr15{j
vZu6%N>;W4z{qmO`Fz@$Xwr^Pf^9@V^k6llBD$A9w00000NkvXXu0mjf$%3sA

literal 0
HcmV?d00001

diff --git a/data/light/ic_moderator_audio_muted.png b/data/light/ic_moderator_audio_muted.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a366edca84178687342885a95214e9606379489
GIT binary patch
literal 638
zcmV-^0)hRBP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px%I7vi7R7efw)jMbuQ4|2sY&0=3#t$e62GkTHXeCN))W#2psAwUka4R7oC|HWN
zA%a3G3!8v~uol`WvGJ2iZG>oHK+^ajs2EB7#Kf%UhU~JLnPs=I(hG;#cjvzM=FR7A
zL&JX&#bR+UcX`b{PW-in8|)mjyx|tZMD%A(B7cfQS?5wYg`NDQKvNVr=D0${uF6i@
z7l@1LuFO~ct?)Dv9hF7eO?;!bl=r)j8S<rE&EtHhA6bRYz2Y{pqH9cXELpoanMkyA
z+Qme!)Qo&1H;91`=W@A0#|4p^z*Q_#(Z#a`mFY%IzLR#&^N`wMf(?wbpUZqt_L5u5
z1n~kK<ReFub<vI`CL@-<g308GXd5WbJzxos7-kt0q#|{TQWX^mgRjwy`@E7-G6$6}
zN3|9FQn8$`G#j{;b$&mgg?Rtl7^jtHqE9NaCYVz7f$cme9(oT$#4Edp^~8j6eh1UM
zr*^o~7UKOLB(D4^zZqp8J;XzeeW{%RhKSo%UnuV43s?Ef7?lwN#x`=6NsiPv@_dyJ
zjTDG0A7M2e#0p~IHqH<eUnibRX2i#m3LOqe-r*&kyyFEUY-9^t>8H;ZocdPev%}!W
ziG3yZmsrU*E^(jODIe3zZsvB_?2$6EI^>BRu`hXAF23+-Vs&*#`9ssOTdx5wTJqdB
zEEliwN!Ah(w=|x^X2(wy)Xq3ojq)?lDgK34>2R}DEf*hCv^SM<jxp&hW8~Gpb^M3p
Y58>C_>;j)!Z~y=R07*qoM6N<$g3+ZWJpcdz

literal 0
HcmV?d00001

diff --git a/data/light/ic_moderator_audio_unmuted.png b/data/light/ic_moderator_audio_unmuted.png
new file mode 100644
index 0000000000000000000000000000000000000000..dd90a2b81d424e0049ff315fcba5c22f8e5870ac
GIT binary patch
literal 541
zcmV+&0^<FNP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px$*GWV{R7efw)J-TvQ53-Oc{66rG`^NoM)^vLg$2=+jYzSuu#lC7lu~44ZzDyS
zwT*>RV`HHvRu+>iY?KwHEPOOk$j~sF=YRClW6peJtT^@Sy?5?C_jKR9xemv_2-7rs
zv4s;vG4g90XR#TiafS^XLn2&s6BemQ5)rs%L9PatA<<n_6Bd~GSjIGi_Jzo~@q`6?
zUb}Z{Nn5Ug`=ntQ7q*;(=M>N9Xtd|G`+N-yicGE0%FB4p$Y{W}T?nBhXQegBJSV!~
z(dK6~U{VdD=+O$4Ts66E3@Bp`t%1CfN+h&`EV(lBkJ^~lK%4jDoYp{g@QMmNz{C6X
zas3`uB<>+&^8TeJBeVuD%(_N94&lNuK7)oK9^~E2cn9ROy+Zz=*o#f<!iPO1FbDa%
zCUFBfm&&n*7-UC<1(Q2*hgk$52c11+K{-aIA<c&hTiKdLfqdI5kS0s0L=b(Dh5eX9
z9INnVYmoJyNKq+8<Cw=Kj&O=Pw4fDXEOK-_b5$d21DQ<|`3TxD2>D_A5yL)qaA2KF
z+!h%$t3?XZq~OST-dH;rWwn5{qU>0Hhi0y~h_Hh<YfQc5dqsY#*3o3Eme-a>9Ah;2
fa+KTV{gK;uc=$#Mr@w_Q00000NkvXXu0mjf?7;O5

literal 0
HcmV?d00001

diff --git a/data/light/ic_moderator_hangup.png b/data/light/ic_moderator_hangup.png
new file mode 100644
index 0000000000000000000000000000000000000000..c49d0f857be950492635019e39d0eac196c296c6
GIT binary patch
literal 513
zcmV+c0{;DpP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px$yGcYrR7efol}#%|Q5eT()RYgQ46?KF3Gfjt?59NXnwOE1q>$M81SqyP*vj6^
znpnt3uu>C#Kg~6_x#yT`$-jQ?@qeE4+_~pG_uN9^zpKOpj=(Y8!)qPKddAF&su+zF
z3d6+qpa(AD4j$kQI$#h6;2ElL3c1uq)-HU&3G_isWdes_u5+B*QYO}5x@Be~!R1PB
z(r^PKm&L?NAQrP`2)u(!<Xme6<z_v?Suh$lXR$5n9;kD$MsVZp2$Q=YkLF1-e=(Kp
zT%X2T8M%(P88>V!U|ZaqH0v6}S{d2fn;AE2EMZxEE*tuCw)ktUjC_e=lal0R1{Qbi
zV{+52^`+*%b^T&mA+Q4$*pDUQ7h^FWYdaEL#rNiuk$Bp6nS)E@#@wVc>*F=97M}>j
zLY9pZ++<tJLfxrZ>veBD<^43YP2Hnlo0bUplAMFu0B<<vKRM+~8hz2MksK8lj4eK@
znNt<={PoeJBB_&{qz8$S<2pr74V>Fj>Err{?D7vYJjy-N{pA*KZu@x8lI)}U!Rdsn
zIxk$?p%?mL2%ew{xj#7Me#?!wBaXq|{`Du5Ie%QA{_<iGz}ORN00000NkvXXu0mjf
DEd%q0

literal 0
HcmV?d00001

diff --git a/data/light/ic_moderator_maximize.png b/data/light/ic_moderator_maximize.png
new file mode 100644
index 0000000000000000000000000000000000000000..1e231d6824df64eb1b9f0a7690fd656a2e595429
GIT binary patch
literal 407
zcmV;I0cie-P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px$QAtEWR7ef&mCs7UKoEx;wX}`(&r9^+vG3sn=p*?CJ@!x$J@ily3Kaw+iYQ(L
zU!bQxfrmo3-z+;}Xe=aIJ?X&5%<S&`CTw<-M&sXpV+Wjb8EmpoCKBztqd`Ug#QI!>
zIE8-DI7*Vlg*e{vw7YN*xy7pn1r)pR5R@nQSBZb50Xlo|D1z7+n&9*31_<rLGl*S+
zf0y|928bNO3y6(iTc4OXt^opVcmuH^Y^k_-@-xWnSf!Za9xPx0_Lx@EDJ({;1t%b<
zrdwE0W`IID6@`n(m~6`}h@F<v`gRS)Gq|vm<SE>N*c3W;zEbQZI0afnZg1!fd0Xdd
zdE0<kwo5-~*Tk(_qP9pI;157Ak-wpKUF|qD_`v(`3H}NSFFm$F=;O`_@*7%Pl*0V7
zh_Vry6E98v&Nu$p<fTKq1!Jo8r?D>ZwLb87@)anec3bghO~wEK002ovPDHLkV1kl|
BwnYE{

literal 0
HcmV?d00001

diff --git a/data/light/ic_moderator_minimize.png b/data/light/ic_moderator_minimize.png
new file mode 100644
index 0000000000000000000000000000000000000000..144cfb69044b3afcdc5f08f42c8aae0088723e1a
GIT binary patch
literal 427
zcmV;c0aX5pP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px$Wl2OqR7ef&l)ElOQ51#`8H`IrM8ZR;6bd5I=qbb$o<Snf%?s#grcod+iH1Z$
z+zZVV8c(3}3gR*@-ydgBww<$!y)sdVKlx@~*T2rbO_u%Z7k8k8Tsv?FZPvW~1YW^(
zV9-hX7(U@77)xqS<|eCNRGIo|-hV;a?Tuw6zhS7$OkX|&XD|oP&{TV~mR{*4MB5>A
zm9y}}oOZI@7S)Kn1?{<3pfhNRT!MGpWPSv?JyBWDKwH~@C)mi{m2e-f^R}rNCF%oY
zR>W558a{{-CM-bFvR~-mg)Y#02&IG%pbv<u%1F_dxxS@nWMm*bgEi*egIj^F;yMh0
z)Uw+mvp#TF@sYY#u>whoJv8m{IZT1>`7)SPlwbg-OPGK|n1@#|DNHbki|+9_96$x$
zz$`z34)O-ZU=J2Sk4bV#T>0Ib_*-Yb4ssLx#IosuDh#V()i#0ppAR3O)$fnQeE|%i
VNRIDE@Kpc+002ovPDHLkV1nu(yFCB^

literal 0
HcmV?d00001

diff --git a/data/light/ic_star.png b/data/light/ic_star.png
new file mode 100644
index 0000000000000000000000000000000000000000..f24a13afe077d523d9f3c982d0274e183bb80320
GIT binary patch
literal 911
zcmV;A191F_P)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!000A4Nkl<ZcmeI&
z0YqeL6ae5KgT){`gvH<?EJ6rjS%eT4VGu$HA%qYXVG+V2gh2?45JCup5JG@K7(9f*
zVDLS>$FRqpTVrnBY3>K`!_2++{OA1VKWP#wl}e>jsbB?-jO7Y0@H15+e8vFJ_>^W`
zfFncOX{drv=o)%RLlrbdE9pNK+=(tyB_%A2V1Uon1EM9{QJQ6#M_;z?Os9Mt$k$A>
z2sJ#(cb8@nzKc5OiI&r>!L4WyC!$uGC0G>oF^7eWwfQtF@Jn<$Tz4%xNV5QQ=!+K9
z|FACVqLw~7?1|bj!|8}N(nt5wKaPFTZTiTt_R`S7U+9aLrmfg(U<u!_i9H<Q3|DBQ
zgFc9UqzpKft%G}9;|xEsk4>y%84c8mY@lyq7l$~*Z`_1}ZhV7L=^INXqlXS|@EfN%
z#15KceU(^6Pq8ji4zv}kJ9gM&5naVuNjb2gIFD#dq9yS#fw%OC8?<nO{TJ_Z!w|p3
zZQRqwa)WxB1k`aQTOU8LizdF}3+9V&_G@4nYuLmAPB4(Ig<26A>r}Q&)YCgd9XGNa
zrXpcqw!8N{RHA`L5e%@I-U2qz7r_%2(i_7Px+3V|>w63ul;Q_r9X%0ru#nyZ7T)`l
z#tsH03;A$nx_#?S5j7l_IBA4*d;V5w6?I(5c7j^TCp3Z^%uVPntu2B9_EMQ)3j+}x
z7kCrf!}nBb`3K!*L3LPn>aK=xFKQGt>~NW;3F<M-bSIY(LXrBMrU_O>=LKInE~aUL
zL(y)**>XEg3tYeU4WovAY>nCcZ%t7nKV-088Uf$%XsC^)(U*@wCrtw^iLOS>MxTpd
zfL~d~B_2fcsqC;TI?M>Co_znHnSZ~}R1}K15PkilP4ejVZt*2&boe-x9l9}8)G?2i
z(FXbJjxmSE)J>z|Z_zz=@ocnfEMOUTM(bh&PsEpz$_kra?+F`mkFuTrPz^-irfV=N
zKV0fL&A67=IZS1R$8k2?JXpblm$@qQ<XE0An&TIs`Fk09sf@6mA5IhND7K8YI8E)=
zHol|@%c-Y!QrVy#D_~oxAUPHJc`A+A#ENIAg2gkPPAU_u#8Y_ja=HSIvfX5CrS}xW
lZY@<Rl}e>jsZ=UO`4@rrK9g?5G{OJ?002ovPDHLkV1g@3pGyD$

literal 0
HcmV?d00001

diff --git a/src/CurrentCallVC.mm b/src/CurrentCallVC.mm
index edd7340a..e75ccc3d 100644
--- a/src/CurrentCallVC.mm
+++ b/src/CurrentCallVC.mm
@@ -1306,17 +1306,7 @@ CVPixelBufferRef pixelBufferPreview;
     if (accountInfo_ == nil)
         return;
     auto* callModel = accountInfo_->callModel.get();
-
-    auto convOpt = getConversationFromURI(QString::fromNSString(uri), *accountInfo_->conversationModel);
-    if (!convOpt.has_value()) {
-        return;
-    }
-    lrc::api::conversation::Info& conversation = *convOpt;
-    auto callId = conversation.callId;
-    if (not callModel->hasCall(callId)){
-        return;
-    }
-    callModel->hangUp(callId);
+    callModel->hangupParticipant([self getcallID], QString::fromNSString(uri));
 }
 
 -(void)minimizeParticipant {
@@ -1377,7 +1367,7 @@ CVPixelBufferRef pixelBufferPreview;
 -(BOOL)isParticipantHost:(NSString*)uri {
     if (accountInfo_ == nil)
         return false;
-    if ([self isMasterCall]) {
+    if ([self isMasterCall] ) {
         return accountInfo_->profileInfo.uri == QString::fromNSString(uri);
     }
     auto convOpt = getConversationFromUid(convUid_, *accountInfo_->conversationModel.get());
diff --git a/src/views/ConferenceOverlayView.h b/src/views/ConferenceOverlayView.h
index ab2d9a52..679af939 100644
--- a/src/views/ConferenceOverlayView.h
+++ b/src/views/ConferenceOverlayView.h
@@ -20,6 +20,7 @@
 #import <Cocoa/Cocoa.h>
 #import "GradientView.h"
 #import "IconButton.h"
+#import "CustomBackgroundView.h"
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -60,12 +61,28 @@ struct ConferenceParticipant {
 @property (nonatomic, weak) NSLayoutConstraint* heightConstraint;
 @property (nonatomic, weak) NSLayoutConstraint* centerXConstraint;
 @property (nonatomic, weak) NSLayoutConstraint* centerYConstraint;
-@property GradientView* gradientView;
-@property IconButton* settingsButton;
+@property NSView* backgroundView;
+@property NSView* increasedBackgroundView;
+@property NSStackView* states;
+@property NSStackView* buttonsContainer;
+@property NSStackView* infoContainer;
 @property NSTextView* usernameLabel;
-@property NSMenu *contextualMenu;
 @property (retain, nonatomic) id <ConferenceLayoutDelegate> delegate;
 
+//actions
+@property IconButton* maximize;
+@property IconButton* minimize;
+@property IconButton* hangup;
+@property IconButton* setModerator;
+@property IconButton* muteAudio;
+
+//state
+@property CustomBackgroundView* moderatorState;
+@property CustomBackgroundView* audioState;
+@property CustomBackgroundView* hostState;
+@property CustomBackgroundView* cusp;
+
+
 - (void)configureView;
 - (void)updateViewWithParticipant:(ConferenceParticipant) participant;
 - (void)sizeChanged;
diff --git a/src/views/ConferenceOverlayView.mm b/src/views/ConferenceOverlayView.mm
index b6afaac9..96b56b48 100644
--- a/src/views/ConferenceOverlayView.mm
+++ b/src/views/ConferenceOverlayView.mm
@@ -18,12 +18,14 @@
 */
 
 #import "ConferenceOverlayView.h"
+#import "CustomBackgroundView.h"
 
 @implementation ConferenceOverlayView
-@synthesize contextualMenu;
 
-CGFloat const margin = 6;
-CGFloat const controlSize = 40;
+CGFloat const margin = 2;
+CGFloat const controlSize = 25;
+CGFloat const minWidth = 140;
+CGFloat const minHeight = 80;
 
 - (instancetype)initWithFrame:(NSRect)frame
 {
@@ -32,111 +34,169 @@ CGFloat const controlSize = 40;
         self.translatesAutoresizingMaskIntoConstraints = false;
         [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable | NSViewMinYMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMaxXMargin];
         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sizeChanged) name:NSWindowDidResizeNotification object:nil];
-        self.alphaValue = 0;
+        self.wantsLayer = true;
+        self.layer.masksToBounds = false;
+        [self addViews];
     }
     return self;
 }
 
-- (void)configureView {
-    self.gradientView = [[GradientView alloc] init];
-    self.gradientView.startingColor = [NSColor clearColor];
-    self.gradientView.endingColor = [NSColor blackColor];
-    self.gradientView.angle = 270;
-    self.gradientView.translatesAutoresizingMaskIntoConstraints = false;
-    [self addSubview: self.gradientView];
-    [self.gradientView.widthAnchor constraintEqualToAnchor:self.widthAnchor multiplier: 1].active = TRUE;
-    [self.gradientView.heightAnchor constraintEqualToConstant: controlSize].active = true;
-    [self.gradientView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor].active = true;
-    [self.gradientView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = true;
-    self.settingsButton = [[IconButton alloc] init];
-    self.settingsButton.transparent = true;
-    self.settingsButton.title = @"";
-    NSImage* settingsImage = [NSImage imageNamed: @"ic_more.png"];
-    [self.settingsButton setImage:settingsImage];
-    self.settingsButton.bgColor = [NSColor clearColor];
-    self.settingsButton.imageColor = [NSColor whiteColor];
-    self.settingsButton.imagePressedColor = [NSColor lightGrayColor];
-    self.settingsButton.imageInsets = margin;
-    self.settingsButton.translatesAutoresizingMaskIntoConstraints = false;
-    [self.gradientView addSubview:self.settingsButton];
-
-    [self.settingsButton.widthAnchor constraintEqualToConstant: controlSize].active = TRUE;
-    [self.settingsButton.heightAnchor constraintEqualToConstant: controlSize].active = true;
-    [self.settingsButton.trailingAnchor constraintEqualToAnchor: self.gradientView.trailingAnchor].active = true;
-    [self.settingsButton.bottomAnchor constraintEqualToAnchor:self.gradientView.bottomAnchor].active = true;
-    [self.settingsButton setAction:@selector(triggerMenu:)];
-    [self.settingsButton setTarget:self];
-    BOOL showSettings = [self.delegate isMasterCall] || [self.delegate isCallModerator];
-    [self.settingsButton setHidden: !showSettings];
+- (void)addViews {
+    self.increasedBackgroundView = [[NSView alloc] init];
+    [self.increasedBackgroundView setWantsLayer:  YES];
+    self.increasedBackgroundView.layer.backgroundColor = [[NSColor colorWithCalibratedRed: 0 green: 0 blue: 0 alpha: 0.8] CGColor];
+    self.increasedBackgroundView.translatesAutoresizingMaskIntoConstraints = false;
+    [self addSubview: self.increasedBackgroundView];
+    self.increasedBackgroundView.hidden = true;
+    self.increasedBackgroundView.layer.masksToBounds = true;
+    self.increasedBackgroundView.layer.cornerRadius = 6;
+    
+    self.backgroundView = [[NSView alloc] init];
+    [self.backgroundView setWantsLayer:  YES];
+    self.backgroundView.layer.backgroundColor = [[NSColor colorWithCalibratedRed: 0 green: 0 blue: 0 alpha: 0.6] CGColor];
+    self.backgroundView.translatesAutoresizingMaskIntoConstraints = false;
+    [self addSubview: self.backgroundView];
+    self.backgroundView.hidden = true;
+    
+    //participat state
+    self.audioState = [[CustomBackgroundView alloc] init];
+    self.audioState.backgroundType = RECTANGLE_WITH_ROUNDED_RIGHT_CORNER;
+    [self.audioState.widthAnchor constraintEqualToConstant: controlSize].active = true;
+    [self.audioState.heightAnchor constraintEqualToConstant: controlSize].active = true;
+    NSImage* audioImage = [NSImage imageNamed: @"ic_moderator_audio_muted.png"];
+    self.audioState.image = audioImage;
+    
+    self.moderatorState = [[CustomBackgroundView alloc] init];
+    self.moderatorState.backgroundType = RECTANGLE;
+    [self.moderatorState.widthAnchor constraintEqualToConstant: controlSize].active = true;
+    [self.moderatorState.heightAnchor constraintEqualToConstant: controlSize].active = true;
+    NSImage* moderatorImage = [NSImage imageNamed: @"ic_moderator.png"];
+    self.moderatorState.image = moderatorImage;
+    
+    self.hostState = [[CustomBackgroundView alloc] init];
+    self.hostState.backgroundType = RECTANGLE;
+    [self.hostState.widthAnchor constraintEqualToConstant: controlSize].active = true;
+    [self.hostState.heightAnchor constraintEqualToConstant: controlSize].active = true;
+    NSImage* hostImage = [NSImage imageNamed: @"ic_star.png"];
+    self.hostState.image = hostImage;
+    
+    self.cusp = [[CustomBackgroundView alloc] init];
+    self.cusp.backgroundType = CUSP;
+    [self.cusp.widthAnchor constraintEqualToConstant: 6].active = true;
+    [self.cusp.heightAnchor constraintEqualToConstant: controlSize].active = true;
+    
+    NSArray *statesViews = [NSArray arrayWithObjects: self.hostState, self.moderatorState, self.audioState, self.cusp, nil];
+    self.states = [NSStackView stackViewWithViews: statesViews];
+    self.states.spacing = 0;
+    [self addSubview: self.states];
+    
+    //actions
+    self.maximize = [self getActionbutton];
+    NSImage* maximizeImage = [NSImage imageNamed: @"ic_moderator_maximize.png"];
+    [self.maximize setImage: maximizeImage];
+    [self.maximize setAction:@selector(maximize:)];
+    [self.maximize setTarget:self];
+
+    self.minimize = [self getActionbutton];
+    NSImage* minimizeImage = [NSImage imageNamed: @"ic_moderator_minimize.png"];
+    [self.minimize setImage: minimizeImage];
+    [self.minimize setAction:@selector(minimize:)];
+    [self.minimize setTarget:self];
+    
+    self.hangup = [self getActionbutton];
+    NSImage* hangupImage = [NSImage imageNamed: @"ic_moderator_hangup.png"];
+    [self.hangup setImage: hangupImage];
+    [self.hangup setAction:@selector(finishCall:)];
+    [self.hangup setTarget:self];
+
+    self.setModerator = [self getActionbutton];
+    NSImage* setModeratorImage = [NSImage imageNamed: @"ic_moderator.png"];
+    [self.setModerator setImage: setModeratorImage];
+    [self.setModerator setAction:@selector(setModerator:)];
+    [self.setModerator setTarget:self];
+    
+    self.muteAudio = [self getActionbutton];
+    NSImage* muteAudioImage = [NSImage imageNamed: @"ic_moderator_audio_muted.png"];
+    [self.muteAudio setImage: muteAudioImage];
+    [self.muteAudio setAction:@selector(muteAudio:)];
+    [self.muteAudio setTarget:self];
+    
+    NSArray *actions = [NSArray arrayWithObjects: self.setModerator, self.muteAudio, self.maximize, self.minimize, self.hangup, nil];
+    self.buttonsContainer = [NSStackView stackViewWithViews: actions];
+    self.buttonsContainer.orientation = NSUserInterfaceLayoutOrientationHorizontal;
+    self.buttonsContainer.spacing = 5;
+    
     self.usernameLabel = [[NSTextView alloc] init];
+    self.usernameLabel.alignment = NSTextAlignmentCenter;
     self.usernameLabel.textColor = [NSColor whiteColor];
-    self.usernameLabel.editable = NO;
-    self.usernameLabel.drawsBackground = NO;
-    self.usernameLabel.backgroundColor = [NSColor clearColor];
-    self.usernameLabel.font = [NSFont userFontOfSize: 14.0];
+    self.usernameLabel.editable = false;
+    self.usernameLabel.drawsBackground = false;
+    self.usernameLabel.font = [NSFont userFontOfSize: 13.0];
     self.usernameLabel.translatesAutoresizingMaskIntoConstraints = false;
+    [self.usernameLabel.heightAnchor constraintEqualToConstant: 20].active = true;
+    [self.usernameLabel.widthAnchor constraintGreaterThanOrEqualToConstant: 60].active = true;
     self.usernameLabel.textContainer.maximumNumberOfLines = 1;
-    [self.gradientView addSubview:self.usernameLabel];
+    
+    NSArray* infoItems = [NSArray arrayWithObjects: self.usernameLabel, self.buttonsContainer, nil];
+    
+    self.infoContainer = [NSStackView stackViewWithViews: infoItems];
+    self.infoContainer.orientation = NSUserInterfaceLayoutOrientationVertical;
+    self.infoContainer.spacing = 0;
+    self.infoContainer.distribution = NSStackViewDistributionFillEqually;
+    self.infoContainer.alignment = NSLayoutAttributeCenterX;
+    [self.backgroundView addSubview: self.infoContainer];
+}
 
-    [self.usernameLabel.leadingAnchor constraintEqualToAnchor: self.gradientView.leadingAnchor constant: margin * 2].active = true;
-    [self.usernameLabel.trailingAnchor constraintEqualToAnchor: self.gradientView.trailingAnchor constant: -(controlSize + margin)].active = true;
-    [self.usernameLabel.bottomAnchor constraintEqualToAnchor:self.gradientView.bottomAnchor constant: - margin * 2].active = true;
+- (IconButton*) getActionbutton {
+    IconButton* button = [[IconButton alloc] init];
+    button.transparent = true;
+    [button.widthAnchor constraintEqualToConstant: controlSize].active = true;
+    [button.heightAnchor constraintEqualToConstant: controlSize].active = true;
+    button.title = @"";
+    button.buttonDisableColor = [NSColor lightGrayColor];
+    button.bgColor = [NSColor clearColor];
+    button.imageColor = [NSColor whiteColor];
+    button.imagePressedColor = [NSColor lightGrayColor];
+    button.imageInsets = margin;
+    button.translatesAutoresizingMaskIntoConstraints = false;
+    return button;
 }
 
-- (IBAction)triggerMenu:(id)sender {
-    int layout = [self.delegate getCurrentLayout];
-    if (layout < 0)
-        return;
-    BOOL showConferenceHostOnly = !self.participant.isLocal && [self.delegate isMasterCall];
-    BOOL showHangup = !self.participant.isLocal && [self.delegate isParticipantHost:self.participant.uri];
-    BOOL showMaximized = layout != 2;
-    BOOL showMinimized = !(layout == 0 || (layout == 1 && !self.participant.active));
-    contextualMenu = [[NSMenu alloc] initWithTitle:@""];
-    if (showMinimized) {
-        NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Minimize participant", @"Conference action") action:@selector(minimize:) keyEquivalent:@""];
-        [menuItem setTarget:self];
-        [contextualMenu insertItem:menuItem atIndex:contextualMenu.itemArray.count];
-    }
-    if (showMaximized) {
-        NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Maximize participant", @"Conference action") action:@selector(maximize:) keyEquivalent:@""];
-        [menuItem setTarget:self];
-        [contextualMenu insertItem:menuItem atIndex:contextualMenu.itemArray.count];
-    }
-    if (showConferenceHostOnly) {
-        auto setModeratorTitle = self.participant.isModerator ? NSLocalizedString(@"Unset moderator", @"Conference action") : NSLocalizedString(@"Set moderator", @"Conference action");
-        NSMenuItem *menuItemModerator = [[NSMenuItem alloc] initWithTitle: setModeratorTitle action:@selector(setModerator:) keyEquivalent:@""];
-        [menuItemModerator setTarget:self];
-        [contextualMenu insertItem:menuItemModerator atIndex:contextualMenu.itemArray.count];
-    }
-    auto audioTitle = self.participant.audioModeratorMuted ? NSLocalizedString(@"Unmute audio", @"Conference action") : NSLocalizedString(@"Mute audio", @"Conference action");
-    NSMenuItem *menuItemAudio = [[NSMenuItem alloc] initWithTitle: audioTitle action:@selector(muteAudio:) keyEquivalent:@""];
-    [menuItemAudio setTarget:self];
-    [contextualMenu insertItem:menuItemAudio atIndex:contextualMenu.itemArray.count];
-    if (showHangup) {
-        NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Hangup", @"Conference action") action:@selector(finishCall:) keyEquivalent:@""];
-        [menuItem setTarget:self];
-        [contextualMenu insertItem:menuItem atIndex:contextualMenu.itemArray.count];
-    }
-    [contextualMenu popUpMenuPositioningItem:nil atLocation:[NSEvent mouseLocation] inView:nil];
+- (void)configureView {
+    [self.backgroundView.widthAnchor constraintEqualToAnchor:self.widthAnchor multiplier: 1].active = TRUE;
+    [self.backgroundView.topAnchor constraintEqualToAnchor:self.topAnchor].active = true;
+    [self.backgroundView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor].active = true;
+    [self.backgroundView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = true;
+    
+    [self.states.topAnchor constraintEqualToAnchor:self.topAnchor].active = true;
+    [self.states.leadingAnchor constraintEqualToAnchor:self.leadingAnchor].active = true;
+    
+    [self.infoContainer.centerYAnchor constraintEqualToAnchor:self.centerYAnchor constant:1].active = true;
+    [self.infoContainer.centerXAnchor constraintEqualToAnchor:self.centerXAnchor constant:1].active = true;
+    
+    [self.increasedBackgroundView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor constant:1].active = true;
+    [self.increasedBackgroundView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor constant:1].active = true;
+    [self.increasedBackgroundView.widthAnchor constraintEqualToConstant:minWidth].active = true;
+    [self.increasedBackgroundView.heightAnchor constraintEqualToConstant:minHeight].active = true;
 }
 
-- (void)minimize:(NSMenuItem*) sender {
+- (IBAction)minimize:(id) sender {
     [self.delegate minimizeParticipant];
 }
 
-- (void)maximize:(NSMenuItem*) sender {
+- (IBAction)maximize:(id) sender {
     [self.delegate maximizeParticipant:self.participant.uri active: self.participant.active];
 }
 
-- (void)finishCall:(NSMenuItem*) sender {
+- (IBAction)finishCall:(id) sender {
     [self.delegate hangUpParticipant: self.participant.uri];
 }
 
-- (void)muteAudio:(NSMenuItem*) sender {
+- (IBAction)muteAudio:(id) sender {
     [self.delegate muteParticipantAudio: self.participant.uri state: !self.participant.audioModeratorMuted];
 }
 
-- (void)setModerator:(NSMenuItem*) sender {
+- (IBAction)setModerator:(id) sender {
     [self.delegate setModerator:self.participant.uri state: !self.participant.isModerator];
 }
 
@@ -193,25 +253,66 @@ CGFloat const controlSize = 40;
     self.centerXConstraint.active = YES;
     self.centerYConstraint.active = YES;
     [self layoutSubtreeIfNeeded];
-    CGFloat width = self.frame.size.width;
-    self.usernameLabel.hidden = width < 150;
 }
 
 - (void)updateViewWithParticipant:(ConferenceParticipant) participant {
+    bool sizeChanged = self.participant.width != participant.width || self.participant.hight != participant.hight
+    || self.participant.x != participant.x || self.participant.y != participant.y;
     self.participant = participant;
-    BOOL showSettings = [self.delegate isMasterCall] || [self.delegate isCallModerator];
-    [self.settingsButton setHidden: !showSettings];
+    [self updateButtonsState];
     [self sizeChanged];
     self.usernameLabel.string = self.participant.bestName;
 }
 
+-(void) updateButtonsState {
+    self.audioState.hidden = !self.participant.audioModeratorMuted;
+    self.moderatorState.hidden = !self.participant.isModerator || [self.delegate isParticipantHost: self.participant.uri];
+    self.hostState.hidden = ![self.delegate isParticipantHost: self.participant.uri];
+    self.cusp.hidden = (self.audioState.hidden && self.moderatorState.hidden && self.hostState.hidden);
+    BackgroundType type = self.audioState.hidden ? RECTANGLE_WITH_ROUNDED_RIGHT_CORNER : RECTANGLE;
+    if (!self.moderatorState.hidden && self.moderatorState.backgroundType != type) {
+        self.moderatorState.backgroundType = type;
+        [self.moderatorState setNeedsDisplay:YES];
+    }
+    if (!self.hostState.hidden && self.hostState.backgroundType != type) {
+        self.hostState.backgroundType = type;
+        [self.hostState setNeedsDisplay:YES];
+    }
+    bool couldManageConference = [self.delegate isMasterCall] || [self.delegate isCallModerator];
+    self.buttonsContainer.hidden = !couldManageConference;
+    if (!couldManageConference) {
+        return;
+    }
+    int layout = [self.delegate getCurrentLayout];
+    if (layout < 0)
+        return;
+    BOOL showConferenceHostOnly = !self.participant.isLocal && [self.delegate isMasterCall];
+    BOOL hangupEnabled = ![self.delegate isParticipantHost: self.participant.uri];
+    BOOL showMaximized = layout != 2;
+    BOOL showMinimized = !(layout == 0 || (layout == 1 && !self.participant.active));
+    self.setModerator.enabled = showConferenceHostOnly;
+    self.hangup.enabled = hangupEnabled;
+    self.minimize.hidden = !showMinimized;
+    self.maximize.hidden = !showMaximized;
+    NSImage* muteAudioImage = self.participant.audioModeratorMuted ? [NSImage imageNamed: @"ic_moderator_audio_muted.png"] :
+    [NSImage imageNamed: @"ic_moderator_audio_unmuted.png"];
+    [self.muteAudio setImage: muteAudioImage];
+}
+
 -(void)mouseEntered:(NSEvent *)theEvent {
-    self.alphaValue = 1;
+    self.backgroundView.hidden = NO;
+    auto size1 = self.frame.size;
+    if (size1.width < minWidth && size1.height < minHeight) {
+        self.increasedBackgroundView.hidden = false;
+        self.backgroundView.layer.backgroundColor = [[NSColor clearColor] CGColor];
+    }
     [super mouseEntered:theEvent];
 }
 
 -(void)mouseExited:(NSEvent *)theEvent {
-    self.alphaValue = 0;
+    self.backgroundView.hidden = YES;
+    self.increasedBackgroundView.hidden = true;
+    self.backgroundView.layer.backgroundColor = [[NSColor colorWithCalibratedRed: 0 green: 0 blue: 0 alpha: 0.6] CGColor];
     [super mouseExited:theEvent];
 }
 
diff --git a/src/views/CustomBackgroundView.h b/src/views/CustomBackgroundView.h
new file mode 100644
index 00000000..8426ad2f
--- /dev/null
+++ b/src/views/CustomBackgroundView.h
@@ -0,0 +1,39 @@
+/*
+*  Copyright (C) 2021 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 <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef enum {
+    RECTANGLE = 1,
+    RECTANGLE_WITH_ROUNDED_RIGHT_CORNER,
+    CUSP
+} BackgroundType;
+
+@interface CustomBackgroundView : NSView
+
+@property (nonatomic, strong) NSImage* image;
+@property (nonatomic, strong) NSColor* imageColor;
+@property (nonatomic, strong) NSColor* backgroundColor;
+@property  BackgroundType backgroundType;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/views/CustomBackgroundView.mm b/src/views/CustomBackgroundView.mm
new file mode 100644
index 00000000..f0e81da5
--- /dev/null
+++ b/src/views/CustomBackgroundView.mm
@@ -0,0 +1,98 @@
+/*
+*  Copyright (C) 2021 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 "NSColor+RingTheme.h"
+#import "CustomBackgroundView.h"
+
+#import <QuartzCore/QuartzCore.h>
+
+@implementation CustomBackgroundView
+
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+    [super drawRect:dirtyRect];
+    NSColor *backgroundColor = self.backgroundColor ? self.backgroundColor : [NSColor colorWithCalibratedRed: 0 green: 0 blue: 0 alpha: 0.8];
+    NSColor *backgroundStrokeColor = [NSColor clearColor];
+    NSColor *tintColor = self.imageColor ? self.imageColor : [NSColor whiteColor];
+    
+    NSBezierPath* path;
+    
+    switch (self.backgroundType) {
+        case RECTANGLE: {
+            path = [NSBezierPath bezierPathWithRoundedRect:dirtyRect xRadius:0 yRadius:0];
+            break;
+        }
+        case RECTANGLE_WITH_ROUNDED_RIGHT_CORNER: {
+            path = [[NSBezierPath alloc] init];
+            NSPoint bottomLeft = dirtyRect.origin;
+            NSPoint topLeft = CGPointMake(dirtyRect.origin.x, dirtyRect.size.height);
+            NSPoint topRight = CGPointMake(dirtyRect.size.width, dirtyRect.size.height);
+            NSPoint bottomRight = CGPointMake(dirtyRect.size.width * 0.5, dirtyRect.origin.y);
+            NSPoint middle = CGPointMake(dirtyRect.size.width, dirtyRect.size.height * 0.5);
+            NSPoint controlPoint1 = CGPointMake(dirtyRect.size.width * 0.96, dirtyRect.size.height * 0.01);
+            NSPoint controlPoint2 = CGPointMake(dirtyRect.size.width * 0.98, dirtyRect.size.height * 0.2);
+            [path setLineWidth:1.0];
+            [path moveToPoint:topLeft];
+            [path lineToPoint:bottomLeft];
+            
+            [path lineToPoint:bottomRight];
+            [path curveToPoint:middle controlPoint1:controlPoint1 controlPoint2:controlPoint2];
+            [path lineToPoint:topRight];
+            [path closePath];
+            break;
+        }
+        case CUSP: {
+            path = [[NSBezierPath alloc] init];
+            NSPoint bottomLeft = CGPointMake(dirtyRect.origin.x, dirtyRect.size.height * 0.5);
+            NSPoint topLeft = CGPointMake(dirtyRect.origin.x, dirtyRect.size.height);
+            NSPoint topRight = CGPointMake(dirtyRect.size.width, dirtyRect.size.height);
+            NSPoint controlPoint1 = CGPointMake(dirtyRect.size.width * 0.1, dirtyRect.size.height * 0.7);
+            NSPoint controlPoint2 = CGPointMake(dirtyRect.size.width * 0.01, dirtyRect.size.height * 0.9);
+            [path setLineWidth:1.0];
+            [path moveToPoint:topLeft];
+            [path lineToPoint:bottomLeft];
+            
+            [path curveToPoint:topRight controlPoint1:controlPoint1 controlPoint2:controlPoint2];
+            [path closePath];
+            break;
+        }
+    }
+    
+    [backgroundColor set];
+    [path fill];
+    [[NSColor clearColor] set];
+    [path stroke];
+    
+    if (self.backgroundType == CUSP) {
+        return;
+    }
+    
+    NSRect rectImage = NSInsetRect(dirtyRect, 4, 4);
+    rectImage.size.width = rectImage.size.height;
+    
+    [[NSColor image: self.image tintedWithColor:tintColor] drawInRect:rectImage
+                                                       fromRect:NSZeroRect
+                                                      operation:NSCompositeSourceOver
+                                                       fraction:1.0
+                                                 respectFlipped:YES
+                                                          hints:nil];
+}
+
+@end
-- 
GitLab