From 1eff68813d3b7f7ca2be7767654d8e91f77a461b Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 15 Mar 2023 13:36:32 +0400 Subject: [PATCH] Reuse new speed change control for video. --- .../Resources/icons/player/player_check.png | Bin 281 -> 349 bytes .../icons/player/player_check@2x.png | Bin 476 -> 585 bytes .../icons/player/player_check@3x.png | Bin 695 -> 871 bytes .../icons/player/player_fullscreen.png | Bin 359 -> 387 bytes .../icons/player/player_fullscreen@2x.png | Bin 639 -> 629 bytes .../icons/player/player_fullscreen@3x.png | Bin 919 -> 733 bytes .../icons/player/player_minimize.png | Bin 341 -> 395 bytes .../icons/player/player_minimize@2x.png | Bin 594 -> 643 bytes .../icons/player/player_minimize@3x.png | Bin 879 -> 747 bytes .../Resources/icons/player/player_more.png | Bin 183 -> 217 bytes .../Resources/icons/player/player_more@2x.png | Bin 361 -> 325 bytes .../Resources/icons/player/player_more@3x.png | Bin 566 -> 475 bytes .../Resources/icons/player/player_pause.png | Bin 230 -> 272 bytes .../icons/player/player_pause@2x.png | Bin 310 -> 377 bytes .../icons/player/player_pause@3x.png | Bin 427 -> 555 bytes .../Resources/icons/player/player_pip.png | Bin 319 -> 464 bytes .../Resources/icons/player/player_pip@2x.png | Bin 530 -> 750 bytes .../Resources/icons/player/player_pip@3x.png | Bin 877 -> 986 bytes .../Resources/icons/player/player_play.png | Bin 295 -> 388 bytes .../Resources/icons/player/player_play@2x.png | Bin 478 -> 614 bytes .../Resources/icons/player/player_play@3x.png | Bin 646 -> 976 bytes .../icons/player/player_volume_off.png | Bin 445 -> 354 bytes .../icons/player/player_volume_off@2x.png | Bin 664 -> 584 bytes .../icons/player/player_volume_off@3x.png | Bin 1026 -> 868 bytes .../icons/player/player_volume_on.png | Bin 482 -> 517 bytes .../icons/player/player_volume_on@2x.png | Bin 1151 -> 973 bytes .../icons/player/player_volume_on@3x.png | Bin 1674 -> 1372 bytes .../icons/player/player_volume_small.png | Bin 341 -> 370 bytes .../icons/player/player_volume_small@2x.png | Bin 668 -> 639 bytes .../icons/player/player_volume_small@3x.png | Bin 962 -> 858 bytes Telegram/SourceFiles/core/core_settings.cpp | 26 +- Telegram/SourceFiles/core/core_settings.h | 30 +- .../SourceFiles/media/audio/media_audio.cpp | 4 +- .../SourceFiles/media/audio/media_audio.h | 4 - .../media/audio/media_audio_ffmpeg_loader.cpp | 5 +- Telegram/SourceFiles/media/media_common.h | 28 + .../media/player/media_player.style | 113 ++-- .../media/player/media_player_button.cpp | 98 ++-- .../media/player/media_player_button.h | 37 +- .../media/player/media_player_dropdown.cpp | 493 ++++++++++++++---- .../media/player/media_player_dropdown.h | 102 +++- .../media/player/media_player_instance.cpp | 12 +- .../media/player/media_player_instance.h | 17 +- .../media/player/media_player_widget.cpp | 401 +------------- .../media/player/media_player_widget.h | 14 +- .../streaming/media_streaming_player.cpp | 6 +- .../media/system_media_controls_manager.cpp | 8 +- .../media/system_media_controls_manager.h | 5 +- .../SourceFiles/media/view/media_view.style | 154 +++--- .../media/view/media_view_overlay_widget.cpp | 6 +- .../media/view/media_view_overlay_widget.h | 2 +- .../view/media_view_playback_controls.cpp | 238 +-------- .../media/view/media_view_playback_controls.h | 20 +- Telegram/SourceFiles/overview/overview.style | 1 - Telegram/SourceFiles/ui/menu_icons.style | 26 + Telegram/cmake/td_ui.cmake | 2 + Telegram/lib_ui | 2 +- 57 files changed, 875 insertions(+), 979 deletions(-) create mode 100644 Telegram/SourceFiles/media/media_common.h diff --git a/Telegram/Resources/icons/player/player_check.png b/Telegram/Resources/icons/player/player_check.png index cdba65b47b2fc2f7aa57e753b62867219c38978d..285a76b885fc86e8b910cd5c00288a143a14a895 100644 GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfU9rtu`46zVQ zPLN<#wqTiY@^qVpSaL{;nL;Hpa1`_Ul7TmXcRGb?p%jpHNQC?ZE-Er zwrw+8pc%Z($C2YDRAZ zVUYa%{5*S=&A_UgsO#CM4J5MLJfGF7#R4t{6302 RbDjb6ny0Iu%Q~loCIFngb5;NV delta 265 zcmV+k0rvjg0+|Ak8Gi-<007{3J@^0s00d`2O+f$vv5yPXnRa z!C4SYI7dV%rG#@%`o8zZh2KHc+Tbuv6JU+w=wtj2U)L4dwtxMf;Cgd}cU|X$-)*z+ag%2`6 zJGSxPXK>8rDnWV#+~2`fqLhMpo&kq{oIl+Q`{1^vsr<7G=9(i4M)U^3-#_2E&QEdx P000007XXkitpSz>( zx#PCEk8A9IubXzhomu(-+XluQ1|apXoY~QC`}6O=Pd_cvo4)&QUX4(srpw~YTaPU{ zy0=a_EOW8vc=9U6`AdU3oi((ROr#pqjAjbRXK%e;e7OFhfXU^TCITE4b5Go=H;`yZ zGn-u-arymsN8|bDn;re^)4#WcgtDGZ+W2FGTcF4Vo$03oG@h)h`6Slg*;!MP!Um*(vHssD$i`5ek6p8)* zs>EtRw_3C|E%#_ewlBw&glh)Jjz0y7Git;PqDEunCrJZM2l7OZJD&m zvP`$dfipgRTM)MT?)&mtwKj5-PpX{E-Ws*wW{%m$h%?bG$=|E?@+~|3@WW@%$Z1ET zoeL+aG;;2`nYaC8#hr!E%Wq|vG&u2n`@L6zlD^7&GZwyvIPr}=tMIeBF?xnUgk}BTxVQQawK;s+>`G+ zzcc2KEJ+gd>$Hb*TJje-2^^guB|r%vJ3u3ZY#@3&C4g=rvVQ<2fNUUoJ0*Z_;j6&q za*<5alw?`vF3G-5AG$2df-K8`9Z8aas;WFZ$HSLpIlzxEzVDwyfOY;8o=G^4^Zy0r z;P3Z44?Me9{0Pm#$8pRHFJ9ow@L~mafae#`Gz}C*;f{JIcs_yK?FMbzLQxdpy6$R; z9pTpt7={5|*ME(VYN)CTJkMK{*cpDkfUfHU^p8P*u_Pe?e!T#D+=gNJD+MMqCm{%) zPk>28QM4!_5T0LPNkTCEHi0>bx~}2zc)Y(&hU<-=unca^T6NQ8p{a5Dq~95Sdjey3 z{*n*?j~AF4r>1G(dc97jHj01oynTe;A%Y-)JkKFbQ%10DTNFI7AD}}F{rQv!{VQ>F zY?J^cfb0N`5VC>j?UVqzg~$Sw0J4GT?UVqzg~$R=z?LiLrV*vh00000NkvXXu0mjf DZ6gA`6MbbHUhz;w~m#W5s< z_3iZc=|zbW$FHyYT6}r^O2Mr!CoZ*LP_N{DtY_kl-xA77MI4uYUZPv0&G(U9)D-Uc9XP-Mht$m78Au`W2-haA9uu zt$zm-CM@t$o4jT@7Yh?hsMb^=uGT9t7w^_qRCoj|y_`AG!zEqsOY7pRSr;oG39z%X zvxKTm7VJJM%sxw5JA_wU~1BZoT+>r`Iuh=JxjgQy-rfjaYlFZh!ys9JAT;|8M?KVN+on_5AbC z*4Ebf+<(u`%`uZccrC+(hwboGRLK2Z(mNPW5vAV)VBtJ3HG% zN>JrT>dKI)n3xo+V>fE}+8^iV^!N8?ZLP`?x6ZSgd+z%6@IQ_VOP2;+%rJSw@I&S4 zw{K?W!q$dOpEk{IW%^}zpdn9-cHVw_>~Ue){>Ge5e31vy(h(ws%}9U1zj5oH`6*u| QzB2%Ur>mdKI;Vst0G~*HFaQ7m literal 695 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Ea{HEjtmSN`?>!lvNA9*<$1a| zhEy=Vy|s58qoWA>hr78hE{iT_n9e(BoMGG86u0OzkC0ZY+v?9uUR6uIRHghA*6lPr zxwD)<{cX=~&J!M1DE7!Ph)2~*=%>KoRNVukU^k{_p&7DY}#kz#!U~lMCpoK zJXC$rUwDW~i$PsxK~upY1}%Z(^$VH`?=W0_U1}=Ve>KM}c&?xO{S}k1GRssia9h0a z-1E&pKm6J|_xy7$Pa$m;|AIG-%Pzmaf)-@8(T!TG*TJA@_I(<0i*XGRI{D zvtFbdxO*2m);A%nfGXi|AfkAn-l^? zf~%*<2s3==Jk|X~@!W5rFIqtJ-?Lu|xb(2hLZ+tD(X3tc?!NZ|#+p@aCNq6PmIf{4 zKe7E+euGKS)~L{5xBq{v2>Jat?rW9peS@HW^?mlIxAThmG1S*T4DsN1U}@H7JhF0v zTmuJNI8#TkqAi0U&pH-?r!qW3H>i%$x49-ZB(nA&FeNg0y85}Sb4q9e0BnIHLI3~& diff --git a/Telegram/Resources/icons/player/player_fullscreen.png b/Telegram/Resources/icons/player/player_fullscreen.png index bb3bbd3639117e437202c2bcd9626e74bdd665e6..29f7b1b625a32e627a19b8eb9d05a8881564235d 100644 GIT binary patch delta 371 zcmaFP)XY3VvYv^7fgxXX)p{VsSRCZ;#IWw1%u680B{jk`&DWPf3&`eRU~JE1U;!xs zVksbIU|?Rr$iNJw89`zTnBcM!3z!jXkiti%rE7uITTd6q5DUS#!PZ# zW|?d2gQ^wz74A8H5V)757!moDr>X2)=G}H>MW+KV%rwnfeV%=;G0vZ)^1s-$@p|YX zLH*O4GNq15+`Vk_cGtT1)zSa9h3%O5eCL!+t5$sw@=v?H?e?~_pVv(J)WRIw(xe|S zk2zB0PwDZ>BP-Trm$2wO`ggPc_VI;HuRq;#>L`>^lyS{5T%0JeEc27v@rO2B+t!}m zbVle{%!P^9ewl@qw=5Cdkhgfjfv#6te`Q{TizKnc-mT?aUR<@M;_{r&sgYfa9A-;M w9+U4V4LU2qyr498rNos*_jesId+hO>DfB_x+>1|`e}RI))78&qol`;+0KdwR^Z)<= delta 343 zcmV-d0jU0i1Lp#e8Gi-<007{3J@^0s00d`2O+f$vv5yProR{`f7x~|)S2v3q^2cajzcnMWkI|0nFsw$YK3F0_bK_G6I_Iy-h zFMtO<&!b>e?6ZLH`_w_xm@aTFGfh*N=Na%y5(b*YbOAI94MSL#Ww34AZfM)KCNOG` zE+Fq)(=;c17*GZX!;m^`5~BsA(2sCT;8~z33X?3K_B;zTO;BlU_e&?IJ$^sB=O}EGd1Ko#?Gb0@yWj5{16I8Ved{cG zm_0kCzd%X&)0aOr96vuQLq)~kKl|A;>r6B4>`I;C}cS<@?4z4 zmSySog6XVtv;IY`4qI(EX_HNB zcXs;w&&;-!A%swNrM6{LOa2CT0!(MnWx!=1VFX+WeG|aJV1Gc)=QE*{qOR$58tYzb z1`34&DHe;dP~4#3@AILwhn-m4H6D*y0`LvNrVpOOd?SLtBkA=E02r(Y2{^ABGXiS0 z8r|)7v9)BS0Dxbw*Rk+mV4Z+(;H?uNgTLKwkr}Zj12Xv2=|sETZsdn;jex~s5gB2C z?{qp|tp`^pD}My!ayb+{hvg#JvBML_pGEX~meJK}^&uh{`5@SEIHc_CWRHMGqd_;D z%}d07zo+eX8v}>2(USp)a6X@>tyYUxDisVE$|mzp9qZEBv>vDb5h7&*s?{oCpG zh4vyp3GT}Uz-9ssHkaTeQSe}UI2=N@!FLt@mJ0wN-uxm&@L)TcOzac@a3Dekzg#Y9 zsZ_F80El=z9?*pvz?aKqo$xRhzJx-=z8i+%)d(;MpIpFpyERcZUYRD|Hk-{R8I49z zipH9!hH4Rn#=bI_4*g4Pl%GG|3q$)SlTrsB8WN#S0O*k&JN$uBHIDrTX0sWa3)e1} z3%TF#IAcXt?hlQkK>q9|wuFB6tm#E3KvSTF)3^)>L!;?I>ND^Jq%nsX(6E~S00000 LNkvXXu0mjf>7*QK diff --git a/Telegram/Resources/icons/player/player_fullscreen@3x.png b/Telegram/Resources/icons/player/player_fullscreen@3x.png index 9354611f58a4f59d38ad9c883d201e9ce4600f0d..77ebb58bd4f4e1b9b4328be2e88b6f89269d749d 100644 GIT binary patch literal 733 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbHUhz~t`f;uw;_ z`gWFK-ys75*P9cVE?!=`z_Ce9Rh0FS%|r`E^#w~8EPgIjz_zL+^U_84i41S+G__N1 z_DM!et5{T+I@|Nj_w*SzI;~|j7d2=oaCI=jsn|yS>hJDV)qCS)j{mKjZ*=QznfK){ zHFm*0v$u3E(m1{8efp`VMV^zEob(Jn7PtQTd56EZnsN;!0^Z37^!(Y#dAPtv?zgf0 zX`eWbqo=Yz|GZOVy;$3%%I?a<=RZ@7R(i`h3akm!Ryy3C_UC(|K>qK)``*c)n0n2Z zHELQy*s8-17gY7j&zQ<(xHCdWqWNmzLV3MMT+bFbX#vS)kGQ@~=u(wv?UI+6*d>1> zZ+mdD?5h)R7O-}PS*Z43%y3!4^rXZpwD;nb6Si4d38nj1rSQa*!x(`*|MJZReJgLj4GMWa(MCfh hNf|ksH7;1O)-g1yH~wV`uJ{8=TAr?cF6*2UngD}ADB}PC delta 908 zcmV;719SY{1(yeq8Gi-<0033(vqt~`00d`2O+f$vv5yPXl zMy$>GT`p(5-1`E@9r-S~d_UvmJR^h<$~G(x0)HIpvHDv8fq#IhfItWY2moQ6K?np0 z0AZX#2m}ZKVVpq-1PB0OoIzm#DijK2Hk%PjspoC8*^p|r3UJ87{Ksmbw_dNuZ1f95 zQqI$guh`4Wi~OTitc^yaiY>lQS5{@sjJJ7ye%7BttOpNcH=nGBLGBat1Ifd+?E&J+ z=^}+5NEhk90Dsa!QU$VN6=O;LD#>K>SKXy5yxiHZ+NNHwdtT8PqUzX{8>n8u*YkC7 zxN4|@ba6rr(;4??y z2mmJ}5m(p;a}|rlVsy1y9Xm*mWjG>v8Ip)8?1KrEN`IxKAjg&Ga0I|H();^+c$WD{ zBtnP7;jzb$V>dVgsZ=V_e!ox4<#Kp{cyifnmhw;iw5Z?+B#nJAzhWYxhldCH_V#u# z8Vm;X_V(7xOz_ku@;wl?3vNjy5+s+)k@b2_y4^0>?RLfnYXy)o4atS9ecd6+2x||yWRTQ|D`(fwEVHbD7DLEGITPT9G2_RXhiSs z?v(may{1p+t0n5?qWAarbh%s}aKyoMIyL%C<7MoujT#VtT<4GN{PEpQTPGZW)NZ$3 z_2cGR7dl4`h&`-{-1dLsof{O#Su7TZ^JNDgAAcW||L!{1&y{0pK&MIaU(2!t+~DxO z#^bTHy{ykG)#(B8Bu|mt;_$Zld_Md@$4DwTPft%l$Km7CpFtdmmsYC<-gCarq_k40 z*ns0NkVqcdTZCgC$TA$WKo;Vd1F{sy43Nb*)X3|Oo0}WHT3TE9G;+N`E0k0G^kGuv zntxY5*F6p&s7|pI$s7>JwGhcHkcCL*fjE-e_eod2|ERBGe!WL77K<$R;1}g(zE-Pg zzigk+=cL(e249Db5dT>PJ|^WE1oRiWFaiM|DFPu7AOM7M1|bk20EBS{ArK${gmDHT i5Fh}AaRvcE{{Y35cyQkAL!kfw002ovP6b4+LSTZ0MzUxC diff --git a/Telegram/Resources/icons/player/player_minimize.png b/Telegram/Resources/icons/player/player_minimize.png index a4d648eb74d83c886cb13e190584135caa3776b7..662c532ec8f0e1abd662b1422b4e24880d04d142 100644 GIT binary patch delta 379 zcmcc0)Xh9WvYv^7fgxXX)p{VsSRCZ;#IWw1%u680B{jk`&DWPf3&`eRU~JE1U;!xs zVksbIU|?Rr$iNJw89`zTnBcM!3z!jXkiti%rE7uIS5Ftm5DUS#AscyH40xW{^EzKp zn6)LO{))~Kh7Q>YMl8_^dsnQOp&0by#pO@xJqJ8oTxy&@>P?tDNB`SRMUH=EOB;4D zt$AKK?|9*wt;=pS zCG%(%J^f^N=XG35qJ-;3i@wZRO(I>6b$s97&e|&Ga@nLL&>-mbuDooe^qQXkk7h1- zvuagK#Oqc)ua(O(Esj04KI%6~<@BaS8Zi^&ew*mOFqe8FTEKN{8K2JgPBXDP8$aAR z#qoZ^dA<4-%XfZ0d)VR8`3v7|rQA6(zWiAEc&p95|4g%2S-GLAilJ_%zGB3s$ zh~X``H}VPoQ-L^+HD{9~Ve>q*x~{c-xxgad8vDLOQ546WG=EKDU01-snR9++-Gk#} zWVCIo;M=xAmStBsuAd7?A%y37PQl#>bY1re?nVGJku@P4xo^?3EYck{W8uZ`GBSCc zidYLT@%>K5CKBo5U(|-M<#C3FZ&)xYgo1I_VvoMmG>xHQ0izyZ3WQ+@(=_=JxWb2F zP(n^J7{^fot3IkaQpTF#KX)R~_x&MImZb)yn#fX6uR#z55Jiy&w*--Qez22;9Zus7 XD#!+A5?fB800000NkvXXu0mjfe`Su$ diff --git a/Telegram/Resources/icons/player/player_minimize@2x.png b/Telegram/Resources/icons/player/player_minimize@2x.png index efbd5b0c9b38c394e254cdd6a54330ca7f3c94a3..13c175a358fc24aafcfdeef52f959b9ab1dc9ead 100644 GIT binary patch delta 629 zcmcb_(#$$RvYv^7f#F6%-UT4VSRCZ;#IWw1%u680B{jk`&DWPf3&`eRU~JE1U;!xs zVksbIU|?Rr$iNJw89`zTnBcMo3z!jXkiy&MIXpmRZ#`WcLn1ie&hTAz$UxwDZs7HA zN=*+K>+ft^(5`XMnSYU-lOum%;0w_^+kcC_`Td#6Im1pWdDR~GT@zl;IQPeDa>_DE zod zelv6UB$c)GFU?d=c5aB#Q=jZ9;wn(lyx`{BvdmeZyjIq~k;pKSGBj6tu+6pDLS}-I zChwHKMi+CXvsHWDt{s`^b6NkwYLmZiCz_VUE7XZ4-+N)a^wfjhzuuPp-o0-2?YGBR z{v>Txj(R_9TlKt#HJNQ?eZ2E`-G2{s+vb}We(YN3zdU%}^Cd6C8}hq)y$;x&JJl_6 z^y8-Un|xj!i+NeMZehLO+2@}PrtE*UJUC5`)Be+a7YFl|7gH9leJIWOZ|nT?&%ajL z&Og6rle=UzYnA=gIHxM#_v!5F@pI0I?J1Kw%Jp96^CgDVmk!hSMd%!vvNmk>%^Wwo zkl(z#F9IEoF4LPE)Ry(?+ETsLJDL;~Qiu7=4`ypL?sHnqd}t*@?a!|JJQ>*$K^_~JjJw delta 580 zcmV-K0=xZ#1=0kN8Gi-<00374`G)`i00d`2O+f$vv5yPiIx7!iZACO5L0XQWB-HdaT3T3H*jQ;|!`pdz%OeQvtO|~C zCqW21?EK%%F6`O>fLI(KYu3jq{}XNi-d#ac0aJk|Ghl`gHGhGl(Fn%lajAQ!(}5re z;B-2H=Xua-wMzYEBYwy#KQ5O`j5eK4b7OV8-FUa#{rbos-}iHUGkU8Rj{(z`g+TG! z?KbU_T#G8GAnFG|brPaX{NZqjEz9BwfDnttqA>I4^EvMK`zrC+{+vE0>Fu_3BeBlA;hvHJxmI(Yg^2%*K2%b zlk9rE!tHh=`8I?X(tWJeR)MNAWB{tlEVbY7r~vM!#D)YH^`v99T2T}Dv>&Mg&1MtY z?KU~sa5yCUzQVEaBOy9Z+VJbBW9errzWx`f3Zw&}>VNBn?Q{SPN`-i#z+@B@^r;7c zokUTj98CqmRAL@!*8`Z(=SuPDz!HEeo+W^ecy<6~;%Bp2?!;y@geH>-CN$SjycC*! zJIVVsoXaQi@px2#mvteyA(^m@Hm{N0^ExUO5L zIAtu-mOaw8ZEze1_WM1AVJMQ$$4CHtjr diff --git a/Telegram/Resources/icons/player/player_minimize@3x.png b/Telegram/Resources/icons/player/player_minimize@3x.png index 16367d5d61f043e05a857e5f35562de1c2a18122..4fde439dd51312126c2a2df0580c4bf41d5a6655 100644 GIT binary patch literal 747 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbHUhz!d7~;uw;_ z`gWG#xh^;o>nRWyJ#gfY|sMhUDt@P02x!;7y~_1~Mg-sY{o8nrfT zdv&Ca*p|WvtKXLWF0gpAZ&A~-^NIm$ufM)y^#90-pFM}a_aA?}XnlrNtyb{E=&fwC zebihHOim~GX&p(qxyj9z|KOaE4$m*wITpX+;#Hf!%EV>a8@aINQ3rX{T3R6|T6LIoq%yX?E0H~n;~%<-@ni)Jo+ znYZ0~{`sfgrmq${U+nU^e8lR(L#g8qZp*%%^qQ8cZ?o?H`}1kW&%_Tmy#4o45?$?c4LYaP$T5Q8SJ&wU{Vv%Iu!IBQKzkkP74 z@B!jyIXyE!*8G?Am}OItcI3koQF&3fo=OYWX%D+u-2x7V|0`VdA>jkV^39(%I{7^M z?)JOIlaQr{W<@&?C?04hel8QCLpoHe>>gTe~DWM4f D{LVoF delta 867 zcmV-p1DyQp1@8us8Gi-<0033(vqt~`00d`2O+f$vv5yPATolah z=YM-~4xgQ!|J@qjBzt?=Z)SGpx8JT=@xU14+D7G!fbGn2I)D5rKtM<;5D)<&0!WxL zh=33QBup7ZK!^YmrVJt=L;wj>2Hi4{+wErYc$_(%PGlNC!?|3JO{Y_~TCLd2%L{Y4 zTt6#ac?4coEx8Q_0}f>}nebpRc-0tfHXARO%lmeRe|o(he|&trs*fWNYvRBl56kQ8 zD~bd-l}hEX*?+hH8;yp#KrquwNEi;Z-|cqXpMKn; zdt*Su;qW35hNB~-(`nxC_jx9hQ41)UOzwZue@f|E93AOK_Qo;@CY8-*`F6YgW>hQ| ztz4c7qT@3T1W2)1jMwXR-fFdYB9Rb1x#io@j~BG7eW?h4X@99xm0|>hTulNZAVdHO tQw9+bB7lS`g9r!_K*E$k1cV46;ScztW6=GrLM-fm^QC?w)JAf;;1%GzanmH`jaL Up}WaujsO4v07*qoM6N<$f<7)m8vpH80V_Z`0e<(c%Gpeg|on~W1;=IXxLyJo7fI8W$o;OIE8pYw=Bu-EtO zh=kWO`R(GpR`w>oUdibpWq4o6&)nlj;9dobaF$r@cHMJ&oTn_m&uTmO&s3N{Hc;6+ zLn*6T_zhQ;6yv%@%d)G_8uhnb(Am7~+|_T_ZfgA#WZBUbwK*rOWY_ew9K7ojoS*Al rTK2rewRg{sTXn)2t%8h#7PAj9D@oWcF%4P10OSQvS3j3^P6ibq2q^h{zG*XlB9_1lNYm_x5Zwg%Fa=VaIRS%#Xx*fZhy}0W$Cq z0hZ7mfb%?u7-Lx1b)Q({S*w8$ncI{^)3$9%4?Kg<^IgYYet*ehLqS{Te%kPT{s2-o zXTo{`pZ5VMVnH+;%d%wl)hPL{an`jft;360(A#et2o^PuiFKnJC{1{wm9j6zQ^dX5 zAOm*{h+x?Z2{5(Sdv##dhU|svk>3HOh_#4I{Z7fJ_p#VjwOuPJ_CiJtuotq_YKvIj zLM>3W?Ae3?_EJK&n~1$o_95hdA&Y_~)?LJfb4~`V(ehJ ri}>v`@BnWkuHQ!nG$L=?!!z&)ImZt#k)JpA00000NkvXXu0mjfRU@Ck diff --git a/Telegram/Resources/icons/player/player_more@3x.png b/Telegram/Resources/icons/player/player_more@3x.png index 0ca0520d6388711c63919ebfd0c7b14213fd5e4c..3ca1794881fd013f868d29590e68c274c897a81a 100644 GIT binary patch literal 475 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}=IZI<7?Q#I zcKSiV76l&H(%l;zPHz%`vhP+MUREvGTj{Vo;Nm{kJwc_vc?)=eZg{YcC28638SEuDv_(a~#9lsp zbJvOm=TC%ewHBL|)|ex8MWa|GP3*)n$6kZu8xt?@opID=L9oG*JwcXiR>95;Oac{Z zjWb@_J!6#p=H<%zcgqt;*0-CI`&ySpD$Hgx?UHSs$+{^sK>18z&W#fNG(-LuB0Prc jL;4!eT|o+lcP#t*%+B6_`)*4cC}2HZ{an^LB{Ts5%l4~3 delta 552 zcmV+@0@wZ91GWT^8Gi-<0033(vqt~`00d`2O+f$vv5yP$A>%=32h=o8x_?|Q>2|xNX`1fC?rqMr zGhFtv8Z!F6U#{2dvUZtfHc`Ltv2P5V>>WioPiO|oYy%<10LqWhlY#s++n)oJbkDVc55TjydW9|XdFlJ(_XU2_Vul8?!o{K!%%$! zk8Sm$4#SWDNPn8IC_X6^D3=0>eRTh6U+^&iC;)01^lR z1Ry~`$av7%0nt$`)It=B1vtEF6yW5?o{pkHC=EJ_B0JDg6o9@(4y%JaT|$rT*5Jbc zdAfui+pPiFdc|P6mrk0m08gP<0f~;{9zo&2HN%4RfoJ?^D*y?600NL8AY?oUK!Sjf q@gM*R0wF@ig8(E52pJC&fc^m$0$$tCPWr|G0000@F_!=64MOE%!w`!T#>4;s002ovPDHLk FV1fg#Lva8A delta 108 zcmV-y0F(ca0_Fjbfn`uhL_t(I5o2H&1;Zr-wr<_}9}IvDh@UV;(EQ4X#)oi$hQP%c z89{0=Mc@h`3??)_+1ycs2nXG07?B%B+qP|kXoY7HOcBCnkE$5-Apihvg(C>&L2Ix8 O0000=lS~wP1m3OoVForzmn<${v@ah!b$?7Y6xVfUNr>tZ; z8gQ;Mj|0Z;DTJ^c52ijoQ@qY_`LmdF5)&CzKCpIrD!QG0!JWzg1fH&bF6*2UngC*b BZr=a^ delta 189 zcmV;u07C!y0=5E>fq$S$L_t(o3GJ6b4uBvG1jXO+|0lpdl!OEuz-);Zjl-#I*R>gY zaoac$fB*y_0D<2Sh%u&=4s*)2h!%jyWBrfnZkjq+5pAf)WBp?tM(Iu|6HHqnTp=FM zG^B}pt6owtkopi1f{-Gh`s@&d6am#|hajW~s6IOcAw@v-7dCb%=RBr$Vt1|nsW?V0 rE1q8*k7xPCJOBX*KmY;|_!NN?e=Cbh5GHt@00000NkvXXu0mjf#+plq diff --git a/Telegram/Resources/icons/player/player_pause@3x.png b/Telegram/Resources/icons/player/player_pause@3x.png index 71d9fd6ebda895afc3fa07b663292a686f385664..7913d8741b4d4c5c005d423b36a5ef8436523c4a 100644 GIT binary patch delta 436 zcmZ3@yqaY~WBpW57srqc*0-}A^I8li`CB^WZl7V-64rRIDBTT5Wn!k6Ir&i zz3%_~`L8ZFe5SykLMFchoED9@IeQ*k_+H+%?)Sd=&wqN^X*eBXlsWwHws<<_aEE<}*IE4ZnApsEW!-evKOaTRH8?U( z?${StRTJjk_DJ6O{`V&iTCvwnd7PvEM^x5`Me~H%NqO;?$nG|_mDlb+|BKJ#5BI5Y1J*0aX)6Q z8@lDlZ!M`x?&zG`X1iS7)?{>U4>9>9w(W+^dCB)%LM{ruU!Acgqp~{x=ZXDK*WCYV zpv&+7_oIf~`Q!i3g*)7P@aHh61*7_hexV)V-*d`yZwuI%D-<`x(1cqnnJb>@P%JiKj7UKO`K4L()< z?v9zAKm!Ad0s|w50|OI`xki0LvEI@*zw5%~HBU`r`65!hykvf>sATNk7fTfwYyaNS zyqlhQwB+i3AEsZwb_VY{@2VR6rgGa8-op`I@3)_sTQ3!Ng+1@@^!TXH8}c`I8uqX% z9`HBp;P@^fQM{N-=tzWs;5$QuXKtb@9UB#t?j$Fi>DBTONOpADas0px>Cg!Z$C^3{ z`Wp?+S1CC5unInuV@_6I5tXfOeSVXqL3i!^#0C7bU;Fo7jXJx}{UXndqmMgu+8XWl tq}YC6{`$^JReqfXn&@6~c(9+-hGCjL53?HMiMI?u;OXk;vd$@?2>{#Td$Rxl diff --git a/Telegram/Resources/icons/player/player_pip.png b/Telegram/Resources/icons/player/player_pip.png index 52e167ba149f1b51bb28e808b03cc7954aa3dc18..155555bbd6be725fea96e52e5658eb1ecd184579 100644 GIT binary patch delta 449 zcmdnbbb)z-WIYoD14F*(s`WsMu{g-xiDBJ2nU_G0OKOB?ny)W|7Ld)sz}TM2zyeYN z#8N=az`(qKk%1XVGlIkxFu`Rd7BC~&Acc=iOVL60PdxeYR z%FEh6>X{TDGXH7uQvE0>6u;n^{)T@Jj=EA)U#LiJG`F6e{pnh@_NP*lrb{UO{67!y)+|-jYYnkUHjV__%8&y0{tbcuLTdqXg=}iKkgp(fxO+$MU^`}0|oZPFlBk;1xXPe|V zmN&NbCQ4j+S!xzUgymvcnuwqq}g;KfY23E-q zMONqEUnVbXRcCH)dT8a&5c9y;z*ktUFXLO{%inVI=4f;^DcGEH-?uLN{`ZTlKg}zy ZHRcOHO`BTFxPTuNpq{RNF6*2UngB`p$Cf@ji5Ag}U#;@sHID6*EwyY%tP(<;t+l0{A=}e)O3IH^&$L#eO^G5tf z0>dzXloGVo(KU{vz0G-^qZrCnO2ITu7E2B_StB{d_YDOMj(@NzLlKL$0-V(My&H`B zjOF6o-H!!LUe}dGi&-F0jCmt^T;P4DV!ZEU`lS;r%W_EU$IYa0_k|GA$w9_T-!Glu zuzW9ARG@8J^C!Ebeyhn|vCuRP)OBqZ$BLeSN!>jV=(^60XZd@%W;#-j9Z%Vrow%?v zUS9{F>*R}imMEOKSfJXRtgFZY2R+D1K7;*zfe(;uch1T0mpcFe002ovPDHLkV1i$i Bjt&3- diff --git a/Telegram/Resources/icons/player/player_pip@2x.png b/Telegram/Resources/icons/player/player_pip@2x.png index bfa8e10f7c47c9380b584f91744f288b8fd347d7..d37c609add3f8551f1e1fdd52488a267033075b9 100644 GIT binary patch delta 737 zcmbQl@{V4=bpURT^mr+dVAMeIZm+mJj|6+dQa`2k|8dh#@R&7@i-6J-e3X}58ch4?J zVA|bVY@DBVZ|~mEdFLJ)oYqJXG2pTZYgG5FUVd4!*KP926axv7L(e`}ZoauEZoSCa zw9TC^haP{Na6Cz-|M=6Qn=ebg{obp(#G=PVY4S;yvrksn2WSK=j9RP49$d2d=90^q zULn#6#a44Q;*$gvGn5-YDi(aI+G{ga{9fzaEE6fc>D&wN=3UR*e*Lv6e{*}TBzI!^ zOhXBtUw`)r@T;Gi=C#y8;lcOc#XDnGU)`j@+EQ@$NRr`$18Q>p`eNMPeIG8oEi<;i z5wbc|v2D)NBFQohk$NS5$@3`_9iMaMn%oRn*H>saU$W=iLpyJWj(02Hm%XyPUl#3f z;+vekRtCq)Cq`T=MTGwFuM9d;XtN|jVe-iiL34dZzV^dszyHjZUU)I%$f`$*OMlLh z>OVf;C2v~p^?>!)S6_dhKYwB=LB;s)t>6L{r1%TbqsBDo6MZd>N{?Eay-58 zY`0&>E1lche3sATJ0?(M@xiF2Tunz}Pa1!~iU^&3`}8I1n*{cOv(#CYW~F$)ca3I0YwkV&%xD%h*DrYGm8V4$m)E#_bb%7 delta 515 zcmV+e0{s2%1(F1i8Gi-<00374`G)`i00d`2O+f$vv5yP-p1hAHdle^cnCOU@GvrODK+GQ51#fx=u(bjcZw!il#}Dh=0f9A;xhu=0Te{zinHQ z=efeU9KeliRl#{V3woPQ=`-L~Z`GR5fU1exk#D!=ey!2hBgY3POK6%Vj*l~Ti^E9(P1CRg z*yFI;!oed2w>1XGX^AINn?oI95Q`i-ekVr>w){H;PL|M)efD2I0Jelof=g$hs;bvF zG`4l+(ho7+@O0lLm(GAraXRJ$po+Ne`wX~NoZ2(WKzx>E6!-SNsZH%`6z@aBFnqg{ zM-23RFQODmAQ diff --git a/Telegram/Resources/icons/player/player_pip@3x.png b/Telegram/Resources/icons/player/player_pip@3x.png index cf0cd7990dc8310ec8b840c91a8e81df3f7d4cc6..d921c88c51278d6c7394b5e5d1368ffdbfae76a5 100644 GIT binary patch delta 975 zcmaFMc8h(2WIYoD1H-L;6#*c{SRCZ;#IWw1%u680B{jk`&DWPf3&`eRU~JE1U;!xs zVksbIU|?Rr$iNJw89`zTnBcM=3z!jXkirRuZtsE0T|8YJLo!(3M(xkmaun$6XJ3>c zc`(7NUizq(h6YRU&B$ebvRgbRD%HCj&{~_2uUuBf&#ju!HN|sfXM!k)mPUqMk)zsc z%XQVqV?Rl(zIpfO*V@h7x8Hkru<=g|W1@qE02`dDW@Gwx_jdK-i!VNYEL0J?x$cwc zCm{IqXOFM0T)(`mZ0q5Nxm;W7w?>^!tKWPu!9c1vV?TG1fyAC&yQ*qxTzLN8zkBy? z+M5`?>+ipRof~lW_wV1IKY#X}-)4I<<kJX;3#IM-@=fl&ds!L@mue|uT24YrgMWzke^6G7;grxJ;?| zz=wT{Zp~=i@^ZHfM}dAK-;7UUX7ex96&zsvtSo;$iOb;niux}nCWf3*UKGo%ELz;_ z)i3jRyXftWoMqGRy)5yPoi}IBg=c<|7w7w^^>i*j5Embxsm1qS|C6<%|MEu#7Bv+W z3vTqJ{%ABmb|*OD#NwcpI%3>vigOh!G^U^4v+VkTU3c>q1{4ID-#FyF{`%v_%3nQ^ zD;cDxHSV?BAK4aB{b9AxSc z@$g~iE|<(piYNAS&X{C%UnIfC-abD>>)Yq7L+L7_kAMEGTpV+B%4dx^3cqIWub-Ok_wT2|?<>mo-?+cE(64**qp#DrI&0;_qpi!nZ;e_D zgl;z?&1d`mJzQ!!B~#j1VC$|OI~KUl@Hlhj{_?qpUkhY6-4!ixX703PVoPopkGRvy zlE1^ZdCHtco8HWgu`Cjo$a*TJ#Xb9`s-cH?VB*#Tx~6|6Pu$hmpu7FwR=eopX1_h_ qH*D%Zp3EF=D2Pbc;8boV`GbLfkLi1d{3Hob_VRS~b6Mw<&;$Ufoxjrn delta 865 zcmV-n1D^ca2ki!s8Gi-<0033(vqt~`00d`2O+f$vv5yPRuBUM1b~?p#DD++ zU}gm|AV2_^SwT(#ipS%k)oO{^Y$i^plaNwAR7azcNF)-L!%L^rqTB6?-EQ|#obTg2 z9*<%^pNn?8Ewb6HW$bGMJ*;YLrBadW^;*Spwp6QC)41VqSS}U|XM8NT0ZIEC6bgkzv)L3rpYK|A zPd!%(da8fJc`O#we$A<$7ErNR6!hEktfFLY>iU%v5{*Vp$6z#S0cA27qo3o)@pvpc zosQ#v?{ydm1hkiE0YxGa(~BpQiAbeVA74G1Z9A2&Cx1Wplt!arD;AB9t~D!2ty(~4 z4ylc1o)qAPVha%6Pk5!+0^}7qlqUlMv(11Q5Fh}|tRMyi2mmuHhyeitz|0B)fc(Jj zDs!z?6I5SWSV4fcD*>q&fneCqnNciP0@`dg&Jh5fP%Kvh8V-kIxm<$O9ye2r!C;_u zbl8l5dK!-EOrmT>CFZGn>!nwS#|!(Ul5%MdG){U_gKr z17bjc05G$H7|=fgw338Yl+c~CQPBz$y3#WWDwoS5nM_{jh0dv6S3CoV7Gjtct9GNW zX8;)q-+N?0fEWW}K!5-+vw|29AOOs)pl1LI27iOVt^3S&$81YMW_P9TL-IAETke>@ zdS8ChzEM<*$1i(-o*B7^dQOqk=~O-Z{?0c``fc9VPXLs+lFd#7J42S^%0>I1)VnBcZ rFtdUf5Fh}|tRMyi2mmuH2mtyEbdfeJD)u?m00000NkvXXu0mjf^wfYq diff --git a/Telegram/Resources/icons/player/player_play.png b/Telegram/Resources/icons/player/player_play.png index 816e5ef1e22bda86e029af4e82f5fb9007952adf..449760247ac9780d51b0bc68176713d3046e66bc 100644 GIT binary patch delta 268 zcmV+n0rUQ+0)zvQfq(8vL_t(I5v|lQ4n;r^08loflemFKhXZIF!d6sD7tlF~d+6Lk zr5A=*|53d680OUH5E%Huem=Q&MNnx?NTP~>Xc*7LkS zIjM+XUDv9rEXzW3ACNfg`#z3ioTY>yc^pU6G_LFFa!-)R&wulrWto=s9TI1|uJe8W z2d4u;&9-fIUE8+($Z0~NY#4?lNz@h!g5rJOqbQOVlMyyxjG3mXxC-?|1g}QZY=X}Z zKV+WgijLe72t5>0+p=6qcY!HM^rqeJD0*a91G^_@}oyN(@Nvu!t7zC7RYiq+ahzV1O zpa?%dKS4E61$bNp40S#}K8g&YPGD0Z`pIz!+~*%ZeuPIhMJHJVm}%FpT}y>b3wPQm cgPI`#0Da={w3RkNqyPW_07*qoM6N<$f@|19KmY&$ diff --git a/Telegram/Resources/icons/player/player_play@2x.png b/Telegram/Resources/icons/player/player_play@2x.png index a4e130c4aeafa51140fad9643e1ce3d3d687458e..152d34fd8a02d831c3fc5f4e4db795d3d3b205d4 100644 GIT binary patch delta 495 zcmVjcDr4#*ZUk(%s%n(cDv2zbB^Oaft5&*7<65CFc{>{ zl<1uj+-x?2AmoUZ;{EJY7>2s8S1OfUu%5ZTjPLh*S(dYBdTE?4Fbtz!uV;ys?uT>s zcsve=!(y?R4SyDnC630nZBZ06V8SuO)Lbr?$z)QkR^P(HG{cu24u^ifPtSz!M5J6S z7L7)O28+msNQ{y`RaHx+5*Zee2ZeaM-L~6p0wxL%(o0YpBS{i26KU&odh|#o0ukQM z#5VE{()F0=D&P0J-7aoNh?=Gl1A3@oz4m}UjDV&NG-$a_r&B1CE1>iPR)Pj7g$0!4 zqh-)|JchCi0<=&Bw3LQbYyeb_L7CoYG=fsFVph^a!w}1I7#BTF(~N*Xl!bYoN1qnY l^UyDd!Z~iL`_-`-fnQ<=U#6ucBGdo?002ovPDHLkV1g4#@gx8M delta 358 zcmV-s0h#{h1l|LXfqzIzL_t(o3GJ3m3WG2Zh5NHs+;|Vsl}B*nO3&cQ+~|oEynqNU z-KcG-5P~r>Y4amxT$JhbCGYifT64SMg#?fQ5yJ?*7&=(&w_iW0wS$ogWC9E@kU21rKpLN*)SfRyhkQfFF+Nb&b-P)%1BNCeB$j2_ zGU=O`JN1JUVHl=qIt)WumgMmSS%MlQfCP{L68JX)Z^e4hEQ9CUV*mgE07*qoM6N<$ Ef`sIytN;K2 diff --git a/Telegram/Resources/icons/player/player_play@3x.png b/Telegram/Resources/icons/player/player_play@3x.png index 0a101f3f65980bdf7a3ea4849d79df537731fb20..35bbf5c5320df9950897adb06c4b02b7d03b5830 100644 GIT binary patch delta 861 zcmV-j1ETzf1<(hOfPVuzNklcgOoZaT@ z>#NV_JLF4s|E?6i>vFk#b#+CxjD0vhAv}E_A0Lm8j~b0;KLVvaJR%NvwcG8xyE{si z>|h;ManSPo{C{jT8h7GQcX<#WYj}Hm3j_kxiQxia`2~!XO6B_cnj4Lxwg9`3d9_+S zJw0i)T8bonq6m>6W$*QRE|-fgZAXD0Hz=FUnoK5IO+?5OE<8VZFJ3gPvH?D~&I z7BjA1uixC<$c~P`ff!&BW7q3-B9YMPbojf=XcYk~et&4c-*>y+GSXv0i>DRsffll6 zvl+u6i%B%5B!~HY9u9|1}BvkFQTCLx20f`MT9MC}H@tAad0LlXytXi!K{;qJ#KExvE z|E~rZ3PiE z>5m`R6D-eUf!Lfo5JAe_<*SNb-MqwgWDhbl#S|@Hk%DC;EToL nY&OH^;%q%<;GBW~B?Er|bbLk0PfPI+00000NkvXXu0mjfYJH>@ delta 529 zcmV+s0`C3L2ZjZZfPVq-NklO{`?(9QEWDw@qc*Sy(~=yp6BIx{-9#N z=ks|!pKGq%@$@K)7!Oz2ZnqXkcez|Rp69B?(LG}{m931f35IL8FFFoDT(v1WF-f)8 zn&_(1Wm+tVP6nfP4E5-0MawtRjZU7SX=EAQVzKD>J)?U1Cb@0f$wk)|Kro(GbONDj zT~wk|Q!k!HB7ZuuM4jH%YE`|Y-`Aoe{pH-{a``?I?o2Pm=?vKK_uT2^t9|*QwIfU2 zwe6-FN28J2Cd!v71^={8zprRLX|N$Mt{fmu5D2Jzevyxo811A4dH zk=V2o|D9U(k_d`jovow`22%;jAQ-KnOo5RLN*|19P=A_Wbc0d`qaKtRm<2(#1+ylo zR$x^2XP9W2LirUacduYV^PAN9_Zoj;JSR>;E#wSYI?i(I{OE!AYHD~VJ`evYzIE&GY36>$r z!Dq8WxN~+~*XXU=;po6tDx`A6r9!Oo?=RC$vQIWN-rTxl$Bl~@C(4{%T$Lfp#Bgrk VmrtD8OV~jE^K|udS?83{1OOuWb=&{| delta 430 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 diff --git a/Telegram/Resources/icons/player/player_volume_on.png b/Telegram/Resources/icons/player/player_volume_on.png index eaddd3674de2815083b687314bd856bd805092f0..8aff2e4da71ebd66566d864794e6ce6ae1a0de1f 100644 GIT binary patch delta 502 zcmaFF+{!XRvYv^7fgxXX)p{VsSRCZ;#IWw1%u680B{jk`&DWPf3&`eRU~JE1U;!xs zVksbIU|?Rr$iNJw89`zTnBcM!3z!jXkiti%rE7u8@;zM~Lo5W}1{wy~WNg(?Txne<8nR%hoL%Yx&MLb^ll3$eQ@C@r9C$fXUTIVPaqRweM;E^l9I; z@5|mTxNrOX_r7Y|dBIwHukR0SNS?3K^(e#SQ1SNLjhPZ|>#u*V{a?3#bL&o>^Pjtq zKKfWO$8WjG#kXbr?am^ut5!W*(c`-_rc8!i@1@c*70+$C_3yv`j#}$>&?#VLh|cL> zWa%Ia5AZ_!`yLt0J-#pr;#&h^*4c~|oZ`SCw*VY_$$srZXHXwUJ8>JDFiM_td68 glYaRATFq6%6r1qoO6>g=uR*ck>FVdQ&MBb@0N|n5V*mgE delta 467 zcmV;^0WAK71mXjb8Gi-<007{3J@^0s00d`2O+f$vv5yPm4+Vn)orWX(BF+t}f_vpqp+i`V78=BOAPni6#;r0B~?Hk;Ftem($o@iU!Oj z3^%t)IpwtH|7%Y_YJ?C-YGviCGUcc6KLyn5b>g}%u`G-9`+xnyEaN5@)>f+p-ENnT zO8{^@9s$e!euw39nHOIyuBs{oK>)t*b0LImHXFFzZV<;Y=(^7PG84`RjK^cRTrPCB z_{jvrY&N5-csw338jW81F@Nuz)Hj(-;Cj7saRz9&+pt(Hc${Gvbc|`5+?Uc70o&~s z!Y~v>(?1v}%YQO;F^D8dG<{JNL9f@#LPS7X>|Hwzz1F&6m(WcYs z?=KpI;ZuRngy(sDb$C42>otEW=JPr4V{uRWlafq_5KPc)HX%8d)Q94S!-1ZAeC$@M zl`uA=^BEu=)Mzwdzu$vx+b|dm^2DLWmXX*>N~wGB$&CI!;2TIb7i9H*)B*qi002ov JPDHLkV1j1P*3kd} diff --git a/Telegram/Resources/icons/player/player_volume_on@2x.png b/Telegram/Resources/icons/player/player_volume_on@2x.png index f931fe24158c86141698f0433383bc1c0165173d..77bf6b06bdd7eb8d6f8f597948f0cae0cceb9bd4 100644 GIT binary patch delta 962 zcmV;z13moz2+aqO8Gix*007uvZqNV#00DDSM?wIu&K&6g002Z~SV?A0O#mtY000O8 z0f%V-1ONa40RR918UO$Q000A^0RRI4000310RRA?0ssU600031001zc0{{d700031 z001xm0002nH*5?500TEkL_t(o3GG+EOL9>d&MdWGni@1zLw{tLLqjdCwm3F8`3JZ* zG!!&M;^I()E-v9o&V{~%#6uo>cnM4*4EaF#bUGB ztW+x7+uJ;o!GBv`8 zxnI-z2jM&s2sDWm9UmXRzP@J39*;*Xtsh*aQia1|fqxEq>{4-hdiwVE7MWO2R;#s3 zW)DMHr@>&5=sN?x7LsRYXL)2{Vd3-h6SESD1dmIKv9Fw*oIF22cd~XtsI*?M$1C>! z{(f|Hq*kj54~0UQUa!{|7Z(YX6r7%(b|DrUqSE|39|!~px7loX88Q9v@Q^@BL9gC0 ziF|2zKz|F_;c(E<&CLyxlgT6@k^(Ugy5oYgmZ>r*tl6>Hk(^n?Yr!P zu_J_L!h!Pq!r07XnlPhT={m&-+r(P$K4E|()jQZV4g2#$C>&eU97To5Ii zmXD7Q;C{cKKuH0P4ruDtC%!ZqMG&8zonbd3ob3X@x3;zj>_6z~aMfru=!AGOAf-|X zYJcwT?s$BlxbOA@tn6fkpq-tafkX%<_tBuC!rtBkw;g3ie}o*o|`$sxgK5L3fiMC4?% z+2!Ts<rb@S%8re-FO5w>P`pE~pNBAUg;{`}_NN#RQ{rJ)*6#SS(^6T<9^xhU5~Y k)Kwf%98erk9FWz4KN4B$Bn6@w2LJ#707*qoM6N<$f+GU0y#N3J delta 1141 zcmV-*1d9932mc6=8Gi-<00374`G)`i00d`2O+f$vv5yP47?^1g#j}|WkO-Vh#^Ttk~izy z>vZ>SzwWu5bH012v+K6kT6e9rf4{x=*{=X01nBS2mf)X79)AYz6NvW+`Xk_vfXg26 zr_fJ3aAszP+~41m_4Rd9U0wataHBtNWHI@bl$3y4wzjq)Cnv{E`>_F!os^xO4WFN% zDhIf`yMwN-E~g~CC(zQ;0^Qx+keHaLf$Hz?2Rel`5?^0mFg!f0`F~?}ct;=@48qdV z5(l@kvSI|3mVcH86B85g^76ucnJ_jsW{j~R;tc^6JOV<1HVs2rSs9$3o-*uKjU&<4 z)@B>0hI<5puaBfZ!Jv79;?o5}{EE zTw!4$TwY#^VL+Ez$;T#-a{_@t0OsfCrG`~SY#4hLmw!^x#^j2M3hkR{Y-}|8vY2q~ zi~xe4o10VdYP#51ZEY<)KR?6J(2x<>)YOy|zq-0=^b>Oi7zg@9*ynl`NR`UOt-|Xxx_d%hzw^#FFzF6BH=BlcysL%f? zNl8g$baYfpzOk_(+30s5=_Vy2L#ZnqDzrhKUmO8U_wn&z9WO+m)YMeV&YIib-{FMcQ zHVJVjU;-uD#8phRGdb=A&d$yl7G8_Jesgof?It0n1k~Q7cLQ>Kd@SL@y#V(ZR-)|z zwgl9VjwSV~#%m=#14-#{v%`;@6}lHO(OBCOI6OQw201l~9rE_}My98ywN$OGt&(kb zcYjyXRVAbvM7_Peos5i(7>^RTyw%s&i|$cnaS9C#43MLvBPk|1IhoL%m)r0tv9q(o z^+)AM+2_W`K&5F%gom zy9*!^F%KT6i;IgT8~eY!yj(j@H#IdGeScX@xOPqe6L@iL$ppF=NZ|2GNssceBM^y1v`c+yX(^nXoJipDQs2?h@m=uVi~#Fpbvi9C zFB^g2i4&hs@OGw_@$qqE3~R$syPra8(&px7=B~As7I$g3ge{MCm zLHC}3p%DuhKB3?gQ47A*_4M>O0go}UBVc;m-QL~?dhc}#KDGp~v*`1>^)|Y>xd}Bj zHEw`gv9_-gI6S|-*dFG7J9xzB6NvXH`6J+ufXg2E^Hb;_7=G^Q6UwdU00000NkvXX Hu0mjfpF1Ie diff --git a/Telegram/Resources/icons/player/player_volume_on@3x.png b/Telegram/Resources/icons/player/player_volume_on@3x.png index 5d10c66a684389cbbc38af996351993c3056d85d..c868accca4d1927c76fd34e95e0d793b762dae5e 100644 GIT binary patch delta 1364 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 delta 325 zcmV-L0lNP30@VVL8Gi-<007{3J@^0s00d`2O+f$vv5yPq~UkL|xq799_MOx9|qW8+rmi1_uXCTzEhvsSiPnkIg2erPSYFq2d4lp~ISe ztdV!{D{#5P##C;jk}0~ zC5xg6(lnLwIPpjTgH-V&fpHw6ZCe$a=OuxrX<%Jf?L%U*sw&vFO-z+uIv85Xb5`0^ zz=gN^qX`jqRX(t5qU$>4*NWO-@9YWc^yd3MxULKAVDFf*z#S;AmAVKo{v&<`E_d+@ XR#r$vI4wRm00000NkvXXu0mjfJf@6K diff --git a/Telegram/Resources/icons/player/player_volume_small@2x.png b/Telegram/Resources/icons/player/player_volume_small@2x.png index 3098bed94ea155715b9fd712d6593653ece5fdac..25ad96230e900782a0d0dd6e62a94fd524a5a10f 100644 GIT binary patch delta 625 zcmbQk`k!TjWIYoD1H+AkybC~zu{g-xiDBJ2nU_G0OKOB?ny)W|7Ld)sz}TM2zyeYN z#8N=az`(qKk%1XVGlIkxFu`RF7BC~&AceQhb9jKtUV6GXhD30_oxXq5K?jjzo%PL$ zJ#Nmf^$S1pnan#l`Gt+6iBxZw(H0Re)}EGurEk3xbgnG zZl(pp(x8=j+kbboF*+5gP5yYs_>A@@9r^y_>YtwJ#oEpFoA0;$^wUdOTb&k8kSo8R zvHfi+J#p)$ci-8Pdv=3Q!jwrTpX6qsJ}E`-VQ$LKm_-RHLI z(~6*#4b1+$r$1;r@E7N_cc(H1^Q~JGrk!#w&3M`Ak1v0(ueFhr=V3c?H^W3m*g$-b zrQxDm1r{o^E@m7NDKG9?%IxD;Yow*w#k3%9ef!N565lty%_!`3Tb#8s=A3us>8O9V z>I4?;H_JHEqWh-CZnbr{P<~tr~;XnGejCFVdQ&MBb@06$R! As{jB1 delta 655 zcmV;A0&xBR1e^ts8Gi-<00374`G)`i00d`2O+f$vv5yPXF z!Y~k>z=EhL_%u)<=#Z#DP$F>vXgC29^dL^a1t|G7hzd@E1oYJ0fUiJad$q|rge1mx z#745EDE`bmzc*v>L<0bXAK$isZ<+rRR{}nrL6rfO0l^5UBYz|qKw)>40pSn|0(g>v zTCE1nW)ni8kRUxacTx*Uc>;j|n$2cNZ;Qo33a$%3Z^#o7m&?T;0!ZYi)2SYK)7vF_ zG6afcfRLX|CMMGK;9m>SG!2c%W16p}PYXz;QYe?p$%s0Q!IuF-el!|c2tQBI0*b{V zI-k#|)9KIzgn#;9cmvy(PN$*U?LsUTv)CjO38+*mmU*{{NF)No;Si3;BjA55H*=*s*JJ;n?S{8Fi8_+PJAl+WjlHQMf-zT-t!C={3?z-olq8>ZoK82bIbG3M{Ir`g4^l&IJ1 z(ChVHmJ;!J9LnXg8O>!x-z%+F3%1*>iwD~>*LBWPiITbjQ!pM<%$-gr)NZ$pIeMM5 z|F^-r0Dt6L;N5aMo6X83Pq6p}5MN(^(CFfwMCdjizQr=26MH4XHy`zOBIfhC?DKBH zBlYsn4*|cWwQ98r(P$J_s}D%PDHLkV1mHUCn5j< diff --git a/Telegram/Resources/icons/player/player_volume_small@3x.png b/Telegram/Resources/icons/player/player_volume_small@3x.png index a10b418aeca6afdfd2d701c02cfe54530cae8375..3cd7173710377f80d4ce60b5db5e54344626e6ec 100644 GIT binary patch literal 858 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbHUhz;wjZ#W5s< z_3g~lt1AOVj;{}2B;f2ik#p%1o-=2rd=i(K{NM2(D~}J)RQm(U#zr4!%+v@zBocV) z_rI{W_wUZFj(J}7ws`JkqlCG&)An9_zWe*lbbC9y56xT)m{u@oHK0%m;jA@w@e2bo zOr&P|9Lw5zip5y0J9qo-^XtziZ7kRsGbz2>MXCMpL+)4q^OpuqG>b5hxRYaMqqU>O zZL-0P+i&Nce?C2AiOZ@4v)Q>uGh-Gv6?ryA>P&lBpy8nEX|Ol${_C$TE0?e@3tRpA z>#y}8Ld?M$B1e-xifL&!-hBHlXl04=)&2ywW+iq1{rhDf z%u?5{ULe)$_Fzxk`pYl3crAToxnUQ}D!=8$x~JJsRdzZ>7izBzTkW-U((}Vlgl=_7 z|EQcjed)yv6xOju~OAm+BU*|5&EW#;{?Ivc%($KjP$G z{Vz*sbZ}UH`J{l3Tz~M@M1_TB!p>X7O?;&MmkU?+A2gc7$+JC3a9M}|UztMqYSCJ2 z^~s)jd_FnXCMS5z5@&iQzo96V>F`>SX}WLAW@r7}b8zjP8+ZH|=U&J$i~nVynYimh zOOI#w-1F%(f)4fx2)D6o)z)P*H2wNp*MI!8L&>)KiO!lvNA9*S9-cQ zhEy=Vo#pRs|Wwd{0!Q#4ezs{U`V_(BI?c(#BmCt6LuS+kSU&_fKVZUOg0LP3QCv6lR z8U!>26qs5B9PTV3np&r&A=T@&HLCUY?Pv>ITi2k{^SWAHee%bTAAj@t^JK|f8@bJ^ zS8E^t91yYlb3tEU-;L|nryqQ_Jb(Lb7LNlz*Upi*n#|7?ekHTxoaT!U9}3iD4rU}W z%F4(rC=e41Fq)aNH!gouXIeS;as};zGa_5IZasSK+O<2k>b~7={`UR*)TzlYyp}EK z?&kjX?c1@k^76Y4Df|oe?zO%4wyd{Q`+Hi0OT)v06JARvEuPS1qxe*egXO`WKQfO# zTIlHO&+g-9YYxy5Nk~pU{AEt1jJCFRz{-#XmtQ)p4V$fgBqn*vI*#_ka`N(1ry9KP ztYHoQ*`fPw_t)RGY9dN)AOG3>g&>%Jb3!lRhX+%BGuu~)*hRt?rX1KXWvc_XG{4Ww|&>8 z@88RJ?B2cE>ddE~ryAtsxOXJd#K>&p@; z-5H(iw`=V9U%!4`uXg$C(@&p-qoxbCJz z@3xmMC9K>(7XL6 zbizi9P*ut2)ho)`CPZwgu> uQr}dn$jQ{=pzgrJq1dp)(~(%}KV!r5Kkth#M&AHtN(N6?KbLh*2~7ZiPrp0> diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 97400a7d2..d1e9bfb5a 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "webrtc/webrtc_create_adm.h" #include "media/player/media_player_instance.h" -#include "media/audio/media_audio.h" +#include "media/media_common.h" #include "ui/gl/gl_detection.h" #include "calls/group/calls_group_common.h" #include "spellcheck/spellcheck_types.h" @@ -119,10 +119,6 @@ void LogPosition(const WindowPosition &position, const QString &name) { return position; } -float64 Settings::PlaybackSpeed::Default() { - return Media::Audio::kSpedUpDefault; -} - Settings::Settings() : _sendSubmitWay(Ui::InputSubmitSettings::Enter) , _floatPlayerColumn(Window::Column::Second) @@ -814,17 +810,17 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _closeToTaskbar = (closeToTaskbar == 1); _customDeviceModel = customDeviceModel; _accountsOrder = accountsOrder; - const auto uncheckedPlayerRepeatMode = static_cast(playerRepeatMode); + const auto uncheckedPlayerRepeatMode = static_cast(playerRepeatMode); switch (uncheckedPlayerRepeatMode) { - case Media::Player::RepeatMode::None: - case Media::Player::RepeatMode::One: - case Media::Player::RepeatMode::All: _playerRepeatMode = uncheckedPlayerRepeatMode; break; + case Media::RepeatMode::None: + case Media::RepeatMode::One: + case Media::RepeatMode::All: _playerRepeatMode = uncheckedPlayerRepeatMode; break; } - const auto uncheckedPlayerOrderMode = static_cast(playerOrderMode); + const auto uncheckedPlayerOrderMode = static_cast(playerOrderMode); switch (uncheckedPlayerOrderMode) { - case Media::Player::OrderMode::Default: - case Media::Player::OrderMode::Reverse: - case Media::Player::OrderMode::Shuffle: _playerOrderMode = uncheckedPlayerOrderMode; break; + case Media::OrderMode::Default: + case Media::OrderMode::Reverse: + case Media::OrderMode::Shuffle: _playerOrderMode = uncheckedPlayerOrderMode; break; } _macWarnBeforeQuit = (macWarnBeforeQuit == 1); _hardwareAcceleratedVideo = (hardwareAcceleratedVideo == 1); @@ -1178,7 +1174,7 @@ float64 Settings::DefaultDialogsWidthRatio() { } qint32 Settings::SerializePlaybackSpeed(PlaybackSpeed speed) { - using namespace Media::Audio; + using namespace Media; const auto value = int(base::SafeRound( std::clamp(speed.value, kSpeedMin, kSpeedMax) * 100)); @@ -1186,7 +1182,7 @@ qint32 Settings::SerializePlaybackSpeed(PlaybackSpeed speed) { } auto Settings::DeserializePlaybackSpeed(qint32 speed) -> PlaybackSpeed { - using namespace Media::Audio; + using namespace Media; auto enabled = true; const auto validate = [&](float64 result) { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index df447d307..a693c6748 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "core/core_settings_proxy.h" +#include "media/media_common.h" #include "window/themes/window_themes_embedded.h" #include "ui/chat/attach/attach_send_files_way.h" #include "platform/platform_notifications_manager.h" @@ -37,11 +38,6 @@ namespace Calls::Group { enum class StickedTooltip; } // namespace Calls::Group -namespace Media::Player { -enum class RepeatMode; -enum class OrderMode; -} // namespace Media::Player - namespace Core { struct WindowPosition { @@ -718,28 +714,28 @@ public: [[nodiscard]] rpl::producer deviceModelChanges() const; [[nodiscard]] rpl::producer deviceModelValue() const; - void setPlayerRepeatMode(Media::Player::RepeatMode mode) { + void setPlayerRepeatMode(Media::RepeatMode mode) { _playerRepeatMode = mode; } - [[nodiscard]] Media::Player::RepeatMode playerRepeatMode() const { + [[nodiscard]] Media::RepeatMode playerRepeatMode() const { return _playerRepeatMode.current(); } - [[nodiscard]] rpl::producer playerRepeatModeValue() const { + [[nodiscard]] rpl::producer playerRepeatModeValue() const { return _playerRepeatMode.value(); } - [[nodiscard]] rpl::producer playerRepeatModeChanges() const { + [[nodiscard]] rpl::producer playerRepeatModeChanges() const { return _playerRepeatMode.changes(); } - void setPlayerOrderMode(Media::Player::OrderMode mode) { + void setPlayerOrderMode(Media::OrderMode mode) { _playerOrderMode = mode; } - [[nodiscard]] Media::Player::OrderMode playerOrderMode() const { + [[nodiscard]] Media::OrderMode playerOrderMode() const { return _playerOrderMode.current(); } - [[nodiscard]] rpl::producer playerOrderModeValue() const { + [[nodiscard]] rpl::producer playerOrderModeValue() const { return _playerOrderMode.value(); } - [[nodiscard]] rpl::producer playerOrderModeChanges() const { + [[nodiscard]] rpl::producer playerOrderModeChanges() const { return _playerOrderMode.changes(); } [[nodiscard]] std::vector accountsOrder() const { @@ -811,9 +807,7 @@ public: [[nodiscard]] static float64 DefaultDialogsWidthRatio(); struct PlaybackSpeed { - [[nodiscard]] static float64 Default(); - - float64 value = Default(); + float64 value = Media::kSpedUpDefault; bool enabled = false; }; [[nodiscard]] static qint32 SerializePlaybackSpeed(PlaybackSpeed speed); @@ -909,8 +903,8 @@ private: base::flags _hiddenGroupCallTooltips; rpl::variable _closeToTaskbar = false; rpl::variable _customDeviceModel; - rpl::variable _playerRepeatMode; - rpl::variable _playerOrderMode; + rpl::variable _playerRepeatMode; + rpl::variable _playerOrderMode; bool _macWarnBeforeQuit = true; std::vector _accountsOrder; #ifdef Q_OS_MAC diff --git a/Telegram/SourceFiles/media/audio/media_audio.cpp b/Telegram/SourceFiles/media/audio/media_audio.cpp index ee5303374..9c6e873a0 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio.cpp @@ -491,7 +491,7 @@ void Mixer::Track::updateWithSpeedPosition() { int64 Mixer::Track::SpeedIndependentPosition( int64 position, float64 speed) { - Expects(speed <= Audio::kSpeedMax); + Expects(speed <= kSpeedMax); return int64(base::SafeRound(position * speed)); } @@ -499,7 +499,7 @@ int64 Mixer::Track::SpeedIndependentPosition( int64 Mixer::Track::SpeedDependentPosition( int64 position, float64 speed) { - Expects(speed >= Audio::kSpeedMin); + Expects(speed >= kSpeedMin); return int64(base::SafeRound(position / speed)); } diff --git a/Telegram/SourceFiles/media/audio/media_audio.h b/Telegram/SourceFiles/media/audio/media_audio.h index 920cd1379..d44b33f8e 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.h +++ b/Telegram/SourceFiles/media/audio/media_audio.h @@ -35,10 +35,6 @@ namespace Audio { class Instance; -inline constexpr auto kSpeedMin = 0.5; -inline constexpr auto kSpeedMax = 2.5; -inline constexpr auto kSpedUpDefault = 1.7; - // Thread: Main. void Start(not_null instance); void Finish(not_null instance); diff --git a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp index f9110b2f3..5f3fc7d41 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp @@ -7,9 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/audio/media_audio_ffmpeg_loader.h" +#include "base/bytes.h" #include "core/file_location.h" #include "ffmpeg/ffmpeg_utility.h" -#include "base/bytes.h" +#include "media/media_common.h" extern "C" { #include @@ -540,7 +541,7 @@ bool AbstractAudioFFMpegLoader::ensureResampleSpaceAvailable(int samples) { } bool AbstractAudioFFMpegLoader::changeSpeedFilter(float64 speed) { - speed = std::clamp(speed, Audio::kSpeedMin, Audio::kSpeedMax); + speed = std::clamp(speed, kSpeedMin, kSpeedMax); if (_filterSpeed == speed) { return false; } diff --git a/Telegram/SourceFiles/media/media_common.h b/Telegram/SourceFiles/media/media_common.h new file mode 100644 index 000000000..73f99109f --- /dev/null +++ b/Telegram/SourceFiles/media/media_common.h @@ -0,0 +1,28 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Media { + +enum class RepeatMode { + None, + One, + All, +}; + +enum class OrderMode { + Default, + Reverse, + Shuffle, +}; + +inline constexpr auto kSpeedMin = 0.5; +inline constexpr auto kSpeedMax = 2.5; +inline constexpr auto kSpedUpDefault = 1.7; + +} // namespace Media diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index f26a53945..df65e4fe2 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -27,31 +27,42 @@ MediaPlayerButton { duration: int; } -MediaSpeedButton { - width: pixels; - height: pixels; - font: font; - icon: icon; -} - MediaSpeedMenu { - menu: Menu; - iconFg: color; - iconFgActive: color; - textFgActive: color; + dropdown: DropdownMenu; activeCheck: icon; activeCheckSkip: pixels; sliderStyle: TextStyle; sliderPadding: margins; sliderWidth: pixels; slider: MediaSlider; + + slow: icon; + slowActive: icon; + normal: icon; + normalActive: icon; + medium: icon; + mediumActive: icon; + fast: icon; + fastActive: icon; + veryFast: icon; + veryFastActive: icon; + superFast: icon; + superFastActive: icon; } -mediaSpeedButton: MediaSpeedButton { - width: 24px; - height: 24px; - font: font(11px semibold); - icon: icon{{ "player/player_speed", menuIconFg }}; +MediaSpeedButton { + size: size; + padding: margins; + font: font; + fg: color; + overFg: color; + activeFg: color; + icon: icon; + ripple: RippleAnimation; + rippleActiveColor: color; + rippleRadius: pixels; + menu: MediaSpeedMenu; + menuAlign: align; } mediaPlayerButton: MediaPlayerButton { @@ -142,13 +153,6 @@ mediaPlayerCancelIcon: icon{ { "player/panel_close", mediaPlayerActiveFg } }; -mediaPlayerSpeedSize: size(30px, 30px); -mediaPlayerSpeedRadius: 4px; -mediaPlayerSpeedRipple: RippleAnimation(defaultRippleAnimation) { - color: lightButtonBgOver; -} -mediaPlayerSpeedDisabledRippleBg: windowBgOver; - mediaPlayerMenu: DropdownMenu(defaultDropdownMenu) { wrap: InnerDropdown(defaultInnerDropdown) { scrollPadding: margins(0px, 4px, 0px, 4px); @@ -157,18 +161,17 @@ mediaPlayerMenu: DropdownMenu(defaultDropdownMenu) { } mediaPlayerMenuCheck: icon {{ "player/player_check", mediaPlayerActiveFg }}; -mediaSpeedMenu: MediaSpeedMenu { - menu: Menu(menuWithIcons) { - separator: MenuSeparator(defaultMenuSeparator) { - padding: margins(0px, 4px, 0px, 4px); - width: 6px; +mediaPlayerSpeedMenu: MediaSpeedMenu { + dropdown: DropdownMenu(mediaPlayerMenu) { + menu: Menu(menuWithIcons) { + separator: MenuSeparator(defaultMenuSeparator) { + padding: margins(0px, 4px, 0px, 4px); + width: 6px; + } + itemPadding: margins(54px, 7px, 54px, 9px); + itemFgDisabled: mediaPlayerActiveFg; } - itemPadding: margins(54px, 7px, 54px, 9px); - itemFgDisabled: mediaPlayerActiveFg; } - iconFg: menuIconColor; - iconFgActive: mediaPlayerActiveFg; - textFgActive: mediaPlayerActiveFg; activeCheck: mediaPlayerMenuCheck; activeCheckSkip: 8px; sliderStyle: TextStyle(defaultTextStyle) { @@ -186,19 +189,37 @@ mediaSpeedMenu: MediaSpeedMenu { width: 6px; seekSize: size(6px, 6px); } + + slow: playerSpeedSlow; + slowActive: playerSpeedSlowActive; + normal: playerSpeedNormal; + normalActive: playerSpeedNormalActive; + medium: playerSpeedMedium; + mediumActive: playerSpeedMediumActive; + fast: playerSpeedFast; + fastActive: playerSpeedFastActive; + veryFast: playerSpeedVeryFast; + veryFastActive: playerSpeedVeryFastActive; + superFast: playerSpeedSuperFast; + superFastActive: playerSpeedSuperFastActive; +} + +mediaPlayerSpeedButton: MediaSpeedButton { + size: size(30px, 30px); + padding: margins(0px, 6px, 0px, 0px); + font: font(11px bold); + fg: menuIconFg; + overFg: menuIconFgOver; + activeFg: mediaPlayerActiveFg; + icon: icon{{ "player/player_speed", menuIconFg }}; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } + rippleActiveColor: lightButtonBgOver; + rippleRadius: 4px; + menu: mediaPlayerSpeedMenu; + menuAlign: align(topright); } -mediaSpeedSlow: icon {{ "player/speed/audiospeed_menu_0.5", menuIconColor }}; -mediaSpeedSlowActive: icon {{ "player/speed/audiospeed_menu_0.5", mediaPlayerActiveFg }}; -mediaSpeedNormal: icon {{ "player/speed/audiospeed_menu_1.0", menuIconColor }}; -mediaSpeedNormalActive: icon {{ "player/speed/audiospeed_menu_1.0", mediaPlayerActiveFg }}; -mediaSpeedMedium: icon {{ "player/speed/audiospeed_menu_1.2", menuIconColor }}; -mediaSpeedMediumActive: icon {{ "player/speed/audiospeed_menu_1.2", mediaPlayerActiveFg }}; -mediaSpeedFast: icon {{ "player/speed/audiospeed_menu_1.5", menuIconColor }}; -mediaSpeedFastActive: icon {{ "player/speed/audiospeed_menu_1.5", mediaPlayerActiveFg }}; -mediaSpeedVeryFast: icon {{ "player/speed/audiospeed_menu_1.7", menuIconColor }}; -mediaSpeedVeryFastActive: icon {{ "player/speed/audiospeed_menu_1.7", mediaPlayerActiveFg }}; -mediaSpeedSuperFast: icon {{ "player/speed/audiospeed_menu_2.0", menuIconColor }}; -mediaSpeedSuperFastActive: icon {{ "player/speed/audiospeed_menu_2.0", mediaPlayerActiveFg }}; mediaPlayerVolumeIcon0: icon { { "player/player_mini_off", mediaPlayerActiveFg }, @@ -292,7 +313,7 @@ mediaPlayerFileLayout: OverviewFileLayout(overviewFileLayout) { mediaPlayerFloatSize: 128px; mediaPlayerFloatMargin: 12px; -mediaPlayerMenuPosition: point(-2px, -2px); +mediaPlayerMenuPosition: point(-2px, -1px); mediaPlayerOrderMenu: Menu(defaultMenu) { itemIconPosition: point(13px, 8px); itemPadding: margins(49px, 9px, 17px, 11px); diff --git a/Telegram/SourceFiles/media/player/media_player_button.cpp b/Telegram/SourceFiles/media/player/media_player_button.cpp index a8e059599..5951443c4 100644 --- a/Telegram/SourceFiles/media/player/media_player_button.cpp +++ b/Telegram/SourceFiles/media/player/media_player_button.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/player/media_player_button.h" +#include "ui/effects/ripple_animation.h" #include "ui/painter.h" #include "styles/style_media_player.h" @@ -263,82 +264,73 @@ SpeedButtonLayout::SpeedButtonLayout( float64 speed) : _st(st) , _speed(speed) -, _oldSpeed(speed) -, _nextSpeed(speed) , _metrics(_st.font->f) , _text(SpeedText(speed)) , _textWidth(_metrics.horizontalAdvance(_text)) -, _oldText(_text) -, _oldTextWidth(_textWidth) , _callback(std::move(callback)) { } void SpeedButtonLayout::setSpeed(float64 speed) { speed = base::SafeRound(speed * 10.) / 10.; - if (_nextSpeed == speed) { - return; - } - - _nextSpeed = speed; - if (!_transformProgress.animating()) { - _oldSpeed = _speed; - _oldColor = _lastPaintColor; - _oldText = _text; - _oldTextWidth = _textWidth; - _speed = _nextSpeed; + if (_speed != speed) { + _speed = speed; _text = SpeedText(_speed); _textWidth = _metrics.horizontalAdvance(_text); - _transformBackward = false; - if (_speed != _speed) { - startTransform(0., 1.); - if (_callback) _callback(); - } - } else if (_oldSpeed == _nextSpeed) { - std::swap(_oldSpeed, _speed); - std::swap(_oldColor, _lastPaintColor); - std::swap(_oldText, _text); - std::swap(_oldTextWidth, _textWidth); - startTransform( - _transformBackward ? 0. : 1., - _transformBackward ? 1. : 0.); - _transformBackward = !_transformBackward; + if (_callback) _callback(); } } -void SpeedButtonLayout::finishTransform() { - _transformProgress.stop(); - _transformBackward = false; - if (_callback) _callback(); -} - -void SpeedButtonLayout::paint(QPainter &p, const QColor &color) { - _lastPaintColor = color; - - _st.icon.paint(p, 0, 0, _st.width, color); +void SpeedButtonLayout::paint(QPainter &p, bool over, bool active) { + const auto &color = active ? _st.activeFg : over ? _st.overFg : _st.fg; + const auto inner = QRect(QPoint(), _st.size).marginsRemoved(_st.padding); + _st.icon.paintInCenter(p, inner, color->c); p.setPen(color); p.setFont(_st.font); p.drawText( - QPointF( - (_st.width - _textWidth) / 2., - (_st.height - _metrics.height()) / 2. + _metrics.ascent()), + QPointF(inner.topLeft()) + QPointF( + (inner.width() - _textWidth) / 2., + (inner.height() - _metrics.height()) / 2. + _metrics.ascent()), _text); } -void SpeedButtonLayout::animationCallback() { - if (!_transformProgress.animating()) { - const auto finalSpeed = _nextSpeed; - _nextSpeed = _speed; - setSpeed(finalSpeed); - } - _callback(); +SpeedButton::SpeedButton(QWidget *parent, const style::MediaSpeedButton &st) +: RippleButton(parent, st.ripple) +, _st(st) +, _layout(st, [=] { update(); }, 2.) +, _isDefault(true) { + resize(_st.size); } -void SpeedButtonLayout::startTransform(float64 from, float64 to) { - // No animation for now. - _transformProgress.stop(); - animationCallback(); +void SpeedButton::setSpeed(float64 speed, anim::type animated) { + _isDefault = (speed == 1.); + _layout.setSpeed(speed); + update(); +} + +void SpeedButton::paintEvent(QPaintEvent *e) { + auto p = QPainter(this); + + paintRipple( + p, + QPoint(_st.padding.left(), _st.padding.top()), + _isDefault ? nullptr : &_st.rippleActiveColor->c); + _layout.paint(p, isOver(), !_isDefault); +} + +QPoint SpeedButton::prepareRippleStartPosition() const { + const auto inner = rect().marginsRemoved(_st.padding); + const auto result = mapFromGlobal(QCursor::pos()) - inner.topLeft(); + return inner.contains(result) + ? result + : DisabledRippleStartPosition(); +} + +QImage SpeedButton::prepareRippleMask() const { + return Ui::RippleAnimation::RoundRectMask( + rect().marginsRemoved(_st.padding).size(), + _st.rippleRadius); } } // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_button.h b/Telegram/SourceFiles/media/player/media_player_button.h index 22e2d6be4..ae2b9563d 100644 --- a/Telegram/SourceFiles/media/player/media_player_button.h +++ b/Telegram/SourceFiles/media/player/media_player_button.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/effects/animations.h" +#include "ui/widgets/buttons.h" #include @@ -60,32 +61,42 @@ public: float64 speed); void setSpeed(float64 speed); - void finishTransform(); - void paint(QPainter &p, const QColor &color); + void paint(QPainter &p, bool over, bool active); private: - void animationCallback(); - void startTransform(float64 from, float64 to); - const style::MediaSpeedButton &_st; float64 _speed = 1.; - float64 _oldSpeed = 1.; - float64 _nextSpeed = 1.; - std::optional _lastPaintColor; - std::optional _oldColor; - Ui::Animations::Simple _transformProgress; - bool _transformBackward = false; QFontMetricsF _metrics; QString _text; float64 _textWidth = 0; - QString _oldText; - float64 _oldTextWidth = 0; Fn _callback; }; +class SpeedButton final : public Ui::RippleButton { +public: + SpeedButton(QWidget *parent, const style::MediaSpeedButton &st); + + [[nodiscard]] const style::MediaSpeedButton &st() const { + return _st; + } + + void setSpeed(float64 speed, anim::type animated = anim::type::normal); + +private: + void paintEvent(QPaintEvent *e) override; + + QPoint prepareRippleStartPosition() const override; + QImage prepareRippleMask() const override; + + const style::MediaSpeedButton &_st; + SpeedButtonLayout _layout; + bool _isDefault = false; + +}; + } // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp index 73afd0dcb..f3a6cb7e3 100644 --- a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp @@ -7,24 +7,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "media/player/media_player_dropdown.h" +#include "base/invoke_queued.h" #include "base/timer.h" #include "lang/lang_keys.h" +#include "media/player/media_player_button.h" #include "ui/cached_round_corners.h" #include "ui/widgets/menu/menu.h" #include "ui/widgets/menu/menu_action.h" #include "ui/widgets/continuous_sliders.h" +#include "ui/widgets/dropdown_menu.h" #include "ui/widgets/shadow.h" #include "ui/painter.h" #include "styles/style_media_player.h" #include "styles/style_widgets.h" -#include "base/debug_log.h" - namespace Media::Player { namespace { -constexpr auto kSpeedMin = 0.5; -constexpr auto kSpeedMax = 2.5; constexpr auto kSpeedDebounceTimeout = crl::time(1000); [[nodiscard]] float64 SpeedToSliderValue(float64 speed) { @@ -87,12 +86,12 @@ SpeedSliderItem::SpeedSliderItem( not_null parent, const style::MediaSpeedMenu &st, rpl::producer value) -: Ui::Menu::ItemBase(parent, st.menu) +: Ui::Menu::ItemBase(parent, st.dropdown.menu) , _slider(base::make_unique_q(this, st.slider)) , _dummyAction(new QAction(parent)) , _st(st) , _height(st.sliderPadding.top() - + st.menu.itemStyle.font->height + + st.dropdown.menu.itemStyle.font->height + st.sliderPadding.bottom()) , _debounceTimer([=] { _debounced.fire(current()); }) { initResizeHook(parent->sizeValue()); @@ -120,11 +119,11 @@ SpeedSliderItem::SpeedSliderItem( ) | rpl::start_with_next([=](const QRect &clip) { auto p = Painter(this); - p.fillRect(clip, _st.menu.itemBg); + p.fillRect(clip, _st.dropdown.menu.itemBg); const auto left = (_st.sliderPadding.left() - _text.maxWidth()) / 2; - const auto top = _st.menu.itemPadding.top(); - p.setPen(_st.menu.itemFg); + const auto top = _st.dropdown.menu.itemPadding.top(); + p.setPen(_st.dropdown.menu.itemFg); _text.drawLeftElided(p, left, top, _text.maxWidth(), width()); }, lifetime()); @@ -172,6 +171,111 @@ SpeedSliderItem::SpeedSliderItem( }); } +void FillSpeedMenu( + not_null menu, + const style::MediaSpeedMenu &st, + rpl::producer value, + Fn callback) { + auto slider = base::make_unique_q( + menu, + st, + rpl::duplicate(value)); + + slider->debouncedChanges( + ) | rpl::start_with_next(callback, slider->lifetime()); + + struct State { + rpl::variable realtime; + }; + const auto state = slider->lifetime().make_state(); + state->realtime = rpl::single( + slider->current() + ) | rpl::then(rpl::merge( + slider->changing(), + slider->changed() + )); + + menu->addAction(std::move(slider)); + menu->addSeparator(&st.dropdown.menu.separator); + + struct SpeedPoint { + float64 speed = 0.; + tr::phrase<> text; + const style::icon &icon; + const style::icon &iconActive; + }; + const auto points = std::vector{ + { + 0.5, + tr::lng_voice_speed_slow, + st.slow, + st.slowActive }, + { + 1.0, + tr::lng_voice_speed_normal, + st.normal, + st.normalActive }, + { + 1.2, + tr::lng_voice_speed_medium, + st.medium, + st.mediumActive }, + { + 1.5, + tr::lng_voice_speed_fast, + st.fast, + st.fastActive }, + { + 1.7, + tr::lng_voice_speed_very_fast, + st.veryFast, + st.veryFastActive }, + { + 2.0, + tr::lng_voice_speed_super_fast, + st.superFast, + st.superFastActive }, + }; + for (const auto &point : points) { + const auto speed = point.speed; + const auto text = point.text(tr::now); + const auto icon = &point.icon; + const auto iconActive = &point.iconActive; + auto action = base::make_unique_q( + menu, + st.dropdown.menu, + Ui::Menu::CreateAction(menu, text, [=] { callback(speed); }), + &point.icon, + &point.icon); + const auto raw = action.get(); + const auto check = Ui::CreateChild(raw); + const auto skip = st.activeCheckSkip; + check->resize(st.activeCheck.size()); + check->paintRequest( + ) | rpl::start_with_next([check, icon = &st.activeCheck] { + auto p = QPainter(check); + icon->paint(p, 0, 0, check->width()); + }, check->lifetime()); + raw->sizeValue( + ) | rpl::start_with_next([=, skip = st.activeCheckSkip](QSize size) { + check->moveToRight( + skip, + (size.height() - check->height()) / 2, + size.width()); + }, check->lifetime()); + check->setAttribute(Qt::WA_TransparentForMouseEvents); + state->realtime.value( + ) | rpl::start_with_next([=](float64 now) { + const auto chosen = (speed == now); + const auto overriden = chosen ? iconActive : icon; + raw->setIcon(overriden, overriden); + raw->action()->setEnabled(!chosen); + check->setVisible(chosen); + }, raw->lifetime()); + menu->addAction(std::move(action)); + } +} + void SpeedSliderItem::setExternalValue(float64 speed) { if (!_slider->isChanging()) { setSliderValue(speed); @@ -379,109 +483,284 @@ bool Dropdown::eventFilter(QObject *obj, QEvent *e) { return false; } -void FillSpeedMenu( - not_null menu, - const style::MediaSpeedMenu &st, - rpl::producer value, - Fn callback) { - auto slider = base::make_unique_q( - menu, - st, - rpl::duplicate(value)); +WithDropdownController::WithDropdownController( + not_null button, + not_null menuParent, + const style::DropdownMenu &menuSt, + Qt::Alignment menuAlign, + Fn menuOverCallback) +: _button(button) +, _menuParent(menuParent) +, _menuSt(menuSt) +, _menuAlign(menuAlign) +, _menuOverCallback(std::move(menuOverCallback)) { + button->events( + ) | rpl::filter([=](not_null e) { + return (e->type() == QEvent::Enter) + || (e->type() == QEvent::Leave); + }) | rpl::start_with_next([=](not_null e) { + _overButton = (e->type() == QEvent::Enter); + if (_overButton) { + InvokeQueued(button, [=] { + if (_overButton) { + showMenu(); + } + }); + } + }, button->lifetime()); +} - slider->debouncedChanges( - ) | rpl::start_with_next(callback, slider->lifetime()); +not_null WithDropdownController::button() const { + return _button; +} - struct State { - rpl::variable realtime; - }; - const auto state = slider->lifetime().make_state(); - state->realtime = rpl::single( - slider->current() - ) | rpl::then(rpl::merge( - slider->changing(), - slider->changed() - )); +Ui::DropdownMenu *WithDropdownController::menu() const { + return _menu.get(); +} - menu->addAction(std::move(slider)); - menu->addSeparator(&st.menu.separator); +void WithDropdownController::updateDropdownGeometry() { + if (!_menu) { + return; + } + const auto bwidth = _button->width(); + const auto bheight = _button->height(); + const auto mwidth = _menu->width(); + const auto mheight = _menu->height(); + const auto padding = _menuSt.wrap.padding; + const auto x = st::mediaPlayerMenuPosition.x(); + const auto y = st::mediaPlayerMenuPosition.y(); + const auto position = _menu->parentWidget()->mapFromGlobal( + _button->mapToGlobal(QPoint()) + ) + [&] { + switch (_menuAlign) { + case style::al_topleft: return QPoint( + -padding.left() - x, + bheight - padding.top() + y); + case style::al_topright: return QPoint( + bwidth - mwidth + padding.right() + x, + bheight - padding.top() + y); + case style::al_bottomright: return QPoint( + bwidth - mwidth + padding.right() + x, + -mheight + padding.bottom() - y); + case style::al_bottomleft: return QPoint( + -padding.left() - x, + -mheight + padding.bottom() - y); + } + Unexpected("Menu align value."); + }(); + _menu->move(position); +} - struct SpeedPoint { - float64 speed = 0.; - tr::phrase<> text; - const style::icon &icon; - const style::icon &iconActive; - }; - const auto points = std::vector{ - { - 0.5, - tr::lng_voice_speed_slow, - st::mediaSpeedSlow, - st::mediaSpeedSlowActive }, - { - 1.0, - tr::lng_voice_speed_normal, - st::mediaSpeedNormal, - st::mediaSpeedNormalActive }, - { - 1.2, - tr::lng_voice_speed_medium, - st::mediaSpeedMedium, - st::mediaSpeedMediumActive }, - { - 1.5, - tr::lng_voice_speed_fast, - st::mediaSpeedFast, - st::mediaSpeedFastActive }, - { - 1.7, - tr::lng_voice_speed_very_fast, - st::mediaSpeedVeryFast, - st::mediaSpeedVeryFastActive }, - { - 2.0, - tr::lng_voice_speed_super_fast, - st::mediaSpeedSuperFast, - st::mediaSpeedSuperFastActive }, - }; - for (const auto &point : points) { - const auto speed = point.speed; - const auto text = point.text(tr::now); - const auto icon = &point.icon; - const auto iconActive = &point.iconActive; - auto action = base::make_unique_q( - menu, - st::mediaSpeedMenu.menu, - Ui::Menu::CreateAction(menu, text, [=] { callback(speed); }), - &point.icon, - &point.icon); - const auto raw = action.get(); - const auto check = Ui::CreateChild(raw); - const auto skip = st.activeCheckSkip; - check->resize(st.activeCheck.size()); - check->paintRequest( - ) | rpl::start_with_next([check, icon = &st.activeCheck] { - auto p = QPainter(check); - icon->paint(p, 0, 0, check->width()); - }, check->lifetime()); - raw->sizeValue( - ) | rpl::start_with_next([=, skip = st.activeCheckSkip](QSize size) { - check->moveToRight( - skip, - (size.height() - check->height()) / 2, - size.width()); - }, check->lifetime()); - check->setAttribute(Qt::WA_TransparentForMouseEvents); - state->realtime.value( - ) | rpl::start_with_next([=](float64 now) { - const auto chosen = (speed == now); - const auto overriden = chosen ? iconActive : icon; - raw->setIcon(overriden, overriden); - raw->action()->setEnabled(!chosen); - check->setVisible(chosen); - }, raw->lifetime()); - menu->addAction(std::move(action)); +void WithDropdownController::hideTemporarily() { + if (_menu && !_menu->isHidden()) { + _temporarilyHidden = true; + _menu->hide(); } } +void WithDropdownController::showBack() { + if (_temporarilyHidden) { + _temporarilyHidden = false; + if (_menu && _menu->isHidden()) { + _menu->show(); + } + } +} + +void WithDropdownController::showMenu() { + if (_menu) { + return; + } + _menu.emplace(_menuParent, _menuSt); + const auto raw = _menu.get(); + _menu->events( + ) | rpl::start_with_next([this](not_null e) { + const auto type = e->type(); + if (type == QEvent::Enter) { + _menuOverCallback(true); + } else if (type == QEvent::Leave) { + _menuOverCallback(false); + } + }, _menu->lifetime()); + _menu->setHiddenCallback([=]{ + Ui::PostponeCall(raw, [this] { + _menu = nullptr; + }); + }); + _button->installEventFilter(raw); + fillMenu(raw); + updateDropdownGeometry(); + const auto origin = [&] { + using Origin = Ui::PanelAnimation::Origin; + switch (_menuAlign) { + case style::al_topleft: return Origin::TopLeft; + case style::al_topright: return Origin::TopRight; + case style::al_bottomright: return Origin::BottomRight; + case style::al_bottomleft: return Origin::BottomLeft; + } + Unexpected("Menu align value."); + }(); + _menu->showAnimated(origin); +} + +OrderController::OrderController( + not_null button, + not_null menuParent, + Fn menuOverCallback, + rpl::producer value, + Fn change) +: WithDropdownController( + button, + menuParent, + st::mediaPlayerMenu, + style::al_topright, + std::move(menuOverCallback)) +, _button(button) +, _appOrder(std::move(value)) +, _change(std::move(change)) { + button->setClickedCallback([=] { + showMenu(); + }); + + _appOrder.value( + ) | rpl::start_with_next([=] { + updateIcon(); + }, button->lifetime()); +} + +void OrderController::fillMenu(not_null menu) { + const auto addOrderAction = [&](OrderMode mode) { + struct Fields { + QString label; + const style::icon &icon; + const style::icon &activeIcon; + }; + const auto active = (_appOrder.current() == mode); + const auto callback = [change = _change, mode, active] { + change(active ? OrderMode::Default : mode); + }; + const auto fields = [&]() -> Fields { + switch (mode) { + case OrderMode::Reverse: return { + .label = tr::lng_audio_player_reverse(tr::now), + .icon = st::mediaPlayerOrderIconReverse, + .activeIcon = st::mediaPlayerOrderIconReverseActive, + }; + case OrderMode::Shuffle: return { + .label = tr::lng_audio_player_shuffle(tr::now), + .icon = st::mediaPlayerOrderIconShuffle, + .activeIcon = st::mediaPlayerOrderIconShuffleActive, + }; + } + Unexpected("Order mode in addOrderAction."); + }(); + menu->addAction(base::make_unique_q( + menu, + (active + ? st::mediaPlayerOrderMenuActive + : st::mediaPlayerOrderMenu), + Ui::Menu::CreateAction(menu, fields.label, callback), + &(active ? fields.activeIcon : fields.icon), + &(active ? fields.activeIcon : fields.icon))); + }; + addOrderAction(OrderMode::Reverse); + addOrderAction(OrderMode::Shuffle); +} + +void OrderController::updateIcon() { + switch (_appOrder.current()) { + case OrderMode::Default: + _button->setIconOverride( + &st::mediaPlayerReverseDisabledIcon, + &st::mediaPlayerReverseDisabledIconOver); + _button->setRippleColorOverride( + &st::mediaPlayerRepeatDisabledRippleBg); + break; + case OrderMode::Reverse: + _button->setIconOverride(&st::mediaPlayerReverseIcon); + _button->setRippleColorOverride(nullptr); + break; + case OrderMode::Shuffle: + _button->setIconOverride(&st::mediaPlayerShuffleIcon); + _button->setRippleColorOverride(nullptr); + break; + } +} + +SpeedController::SpeedController( + not_null button, + not_null menuParent, + Fn menuOverCallback, + Fn value, + Fn change) +: WithDropdownController( + button, + menuParent, + button->st().menu.dropdown, + button->st().menuAlign, + std::move(menuOverCallback)) +, _st(button->st()) +, _lookup(std::move(value)) +, _change(std::move(change)) { + button->setClickedCallback([=] { + toggleDefault(); + save(); + if (const auto current = menu()) { + current->otherEnter(); + } + }); + + setSpeed(_lookup(false)); + _speed = _lookup(true); + + button->setSpeed(_speed, anim::type::instant); + + _speedChanged.events_starting_with( + speed() + ) | rpl::start_with_next([=](float64 speed) { + button->setSpeed(speed); + }, button->lifetime()); +} + +rpl::producer<> SpeedController::saved() const { + return _saved.events(); +} + +float64 SpeedController::speed() const { + return _isDefault ? 1. : _speed; +} + +bool SpeedController::isDefault() const { + return _isDefault; +} + +float64 SpeedController::lastNonDefaultSpeed() const { + return _speed; +} + +void SpeedController::toggleDefault() { + _isDefault = !_isDefault; + _speedChanged.fire(speed()); +} + +void SpeedController::setSpeed(float64 newSpeed) { + if (!(_isDefault = (newSpeed == 1.))) { + _speed = newSpeed; + } + _speedChanged.fire(speed()); +} + +void SpeedController::save() { + _change(speed()); + _saved.fire({}); +} + +void SpeedController::fillMenu(not_null menu) { + FillSpeedMenu( + menu->menu(), + _st.menu, + _speedChanged.events_starting_with(speed()), + [=](float64 speed) { setSpeed(speed); save(); }); +} + } // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.h b/Telegram/SourceFiles/media/player/media_player_dropdown.h index 627c276aa..63197dbb8 100644 --- a/Telegram/SourceFiles/media/player/media_player_dropdown.h +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.h @@ -7,20 +7,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/timer.h" +#include "media/media_common.h" #include "ui/rp_widget.h" #include "ui/effects/animations.h" -#include "base/timer.h" namespace style { struct MediaSpeedMenu; +struct MediaSpeedButton; +struct DropdownMenu; } // namespace style +namespace Ui { +class DropdownMenu; +class AbstractButton; +class IconButton; +} // namespace Ui + namespace Ui::Menu { class Menu; } // namespace Ui::Menu namespace Media::Player { +class SpeedButton; + class Dropdown final : public Ui::RpWidget { public: explicit Dropdown(QWidget *parent); @@ -57,10 +68,89 @@ private: }; -void FillSpeedMenu( - not_null menu, - const style::MediaSpeedMenu &st, - rpl::producer value, - Fn callback); +class WithDropdownController { +public: + WithDropdownController( + not_null button, + not_null menuParent, + const style::DropdownMenu &menuSt, + Qt::Alignment menuAlign, + Fn menuOverCallback); + virtual ~WithDropdownController() = default; + + [[nodiscard]] not_null button() const; + Ui::DropdownMenu *menu() const; + + void updateDropdownGeometry(); + + void hideTemporarily(); + void showBack(); + +protected: + void showMenu(); + +private: + virtual void fillMenu(not_null menu) = 0; + + const not_null _button; + const not_null _menuParent; + const style::DropdownMenu &_menuSt; + const Qt::Alignment _menuAlign = Qt::AlignTop | Qt::AlignRight; + const Fn _menuOverCallback; + base::unique_qptr _menu; + bool _temporarilyHidden = false; + bool _overButton = false; + +}; + +class OrderController final : public WithDropdownController { +public: + OrderController( + not_null button, + not_null menuParent, + Fn menuOverCallback, + rpl::producer value, + Fn change); + +private: + void fillMenu(not_null menu) override; + void updateIcon(); + + const not_null _button; + rpl::variable _appOrder; + Fn _change; + +}; + +class SpeedController final : public WithDropdownController { +public: + SpeedController( + not_null button, + not_null menuParent, + Fn menuOverCallback, + Fn value, + Fn change); + + [[nodiscard]] rpl::producer<> saved() const; + +private: + void fillMenu(not_null menu) override; + + [[nodiscard]] float64 speed() const; + [[nodiscard]] bool isDefault() const; + [[nodiscard]] float64 lastNonDefaultSpeed() const; + void toggleDefault(); + void setSpeed(float64 newSpeed); + void save(); + + const style::MediaSpeedButton &_st; + Fn _lookup; + Fn _change; + float64 _speed = kSpedUpDefault; + bool _isDefault = true; + rpl::event_stream _speedChanged; + rpl::event_stream<> _saved; + +}; } // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 0fe06ffbb..5e1655cc0 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -52,13 +52,6 @@ constexpr auto kRememberShuffledOrderItems = 16; constexpr auto kMinLengthForSavePosition = 20 * TimeId(60); // 20 minutes. -auto VoicePlaybackSpeed() { - return std::clamp( - Core::App().settings().voicePlaybackSpeed(), - Media::Audio::kSpeedMin, - Media::Audio::kSpeedMax); -} - base::options::toggle OptionDisableAutoplayNext({ .id = kOptionDisableAutoplayNext, .name = "Disable auto-play of the next track", @@ -847,7 +840,7 @@ Streaming::PlaybackOptions Instance::streamingOptions( ? Streaming::Mode::Both : Streaming::Mode::Audio; result.speed = audioId.changeablePlaybackSpeed() - ? VoicePlaybackSpeed() + ? Core::App().settings().voicePlaybackSpeed() : 1.; result.audioId = audioId; if (position >= 0) { @@ -1143,7 +1136,8 @@ void Instance::updateVoicePlaybackSpeed() { return; } if (const auto streamed = data->streamed.get()) { - streamed->instance.setSpeed(VoicePlaybackSpeed()); + streamed->instance.setSpeed( + Core::App().settings().voicePlaybackSpeed()); } } } diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index f6879e55f..fce26855b 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -14,6 +14,11 @@ class AudioMsgId; class DocumentData; class History; +namespace Media { +enum class RepeatMode; +enum class OrderMode; +} // namespace Media + namespace Media { namespace Audio { class Instance; @@ -45,18 +50,6 @@ namespace Player { extern const char kOptionDisableAutoplayNext[]; -enum class RepeatMode { - None, - One, - All, -}; - -enum class OrderMode { - Default, - Reverse, - Shuffle, -}; - class Instance; struct TrackState; diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 4c75dfebf..0d375b603 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -42,383 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media { namespace Player { -class WithDropdownController { -public: - WithDropdownController( - not_null button, - not_null menuParent, - Fn menuOverCallback); - virtual ~WithDropdownController() = default; - - [[nodiscard]] not_null button() const; - Ui::DropdownMenu *menu() const; - - void updateDropdownGeometry(); - - void hideTemporarily(); - void showBack(); - -protected: - void showMenu(); - -private: - virtual void fillMenu(not_null menu) = 0; - - const not_null _button; - const not_null _menuParent; - const Fn _menuOverCallback; - base::unique_qptr _menu; - bool _temporarilyHidden = false; - bool _overButton = false; - -}; - -class Widget::SpeedButton final : public Ui::RippleButton { -public: - SpeedButton(QWidget *parent); - - void setSpeed(float64 speed, anim::type animated = anim::type::normal); - -private: - void paintEvent(QPaintEvent *e) override; - - QPoint prepareRippleStartPosition() const override; - QImage prepareRippleMask() const override; - - SpeedButtonLayout _layout; - QPoint _layoutPosition; - bool _isDefault = false; - -}; - -class Widget::OrderController final : public WithDropdownController { -public: - OrderController( - not_null button, - not_null menuParent, - Fn menuOverCallback); - -private: - void fillMenu(not_null menu) override; - void updateIcon(); - - const not_null _button; - -}; - -class Widget::SpeedController final : public WithDropdownController { -public: - SpeedController( - not_null button, - not_null menuParent, - Fn menuOverCallback); - - [[nodiscard]] rpl::producer<> saved() const; - -private: - void fillMenu(not_null menu) override; - - [[nodiscard]] float64 speed() const; - [[nodiscard]] bool isDefault() const; - [[nodiscard]] float64 lastNonDefaultSpeed() const; - void toggleDefault(); - void setSpeed(float64 newSpeed); - void save(); - - float64 _speed = 1.7; - bool _isDefault = true; - rpl::event_stream _speedChanged; - rpl::event_stream<> _saved; - -}; - -WithDropdownController::WithDropdownController( - not_null button, - not_null menuParent, - Fn menuOverCallback) -: _button(button) -, _menuParent(menuParent) -, _menuOverCallback(std::move(menuOverCallback)) { - button->events( - ) | rpl::filter([=](not_null e) { - return (e->type() == QEvent::Enter) - || (e->type() == QEvent::Leave); - }) | rpl::start_with_next([=](not_null e) { - _overButton = (e->type() == QEvent::Enter); - if (_overButton) { - InvokeQueued(button, [=] { - if (_overButton) { - showMenu(); - } - }); - } - }, button->lifetime()); -} - -not_null WithDropdownController::button() const { - return _button; -} - -Ui::DropdownMenu *WithDropdownController::menu() const { - return _menu.get(); -} - -void WithDropdownController::updateDropdownGeometry() { - if (!_menu) { - return; - } - const auto position = _menu->parentWidget()->mapFromGlobal( - _button->mapToGlobal( - QPoint(_button->width(), _button->height()))); - const auto padding = st::mediaPlayerMenu.wrap.padding; - _menu->move(position - - QPoint(_menu->width(), 0) - + QPoint(padding.right(), -padding.top()) - + st::mediaPlayerMenuPosition); -} - -void WithDropdownController::hideTemporarily() { - if (_menu && !_menu->isHidden()) { - _temporarilyHidden = true; - _menu->hide(); - } -} - -void WithDropdownController::showBack() { - if (_temporarilyHidden) { - _temporarilyHidden = false; - if (_menu && _menu->isHidden()) { - _menu->show(); - } - } -} - -void WithDropdownController::showMenu() { - if (_menu) { - return; - } - _menu.emplace(_menuParent, st::mediaPlayerMenu); - const auto raw = _menu.get(); - _menu->events( - ) | rpl::start_with_next([this](not_null e) { - const auto type = e->type(); - if (type == QEvent::Enter) { - _menuOverCallback(true); - } else if (type == QEvent::Leave) { - _menuOverCallback(false); - } - }, _menu->lifetime()); - _menu->setHiddenCallback([=]{ - Ui::PostponeCall(raw, [this] { - _menu = nullptr; - }); - }); - _button->installEventFilter(raw); - fillMenu(raw); - updateDropdownGeometry(); - _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); -} - -Widget::SpeedButton::SpeedButton(QWidget *parent) -: RippleButton(parent, st::mediaPlayerSpeedRipple) -, _layout(st::mediaSpeedButton, [=] { update(); }, 2.) -, _isDefault(true) { - resize(st::mediaPlayerSpeedSize); - _layoutPosition = QPoint( - (st::mediaPlayerSpeedSize.width() - st::mediaSpeedButton.width) / 2, - st::mediaPlayerSpeedSize.height() - st::mediaSpeedButton.height); -} - -void Widget::SpeedButton::setSpeed(float64 speed, anim::type animated) { - _isDefault = (speed == 1.); - _layout.setSpeed(speed); - if (animated == anim::type::instant) { - _layout.finishTransform(); - } - update(); -} - -void Widget::SpeedButton::paintEvent(QPaintEvent *e) { - auto p = QPainter(this); - - const auto innerHeight = st::mediaSpeedButton.icon.height(); - paintRipple( - p, - QPoint(0, height() - innerHeight), - _isDefault ? &st::mediaPlayerSpeedDisabledRippleBg->c : nullptr); - - const auto &color = !_isDefault - ? st::mediaPlayerActiveFg - : isOver() - ? st::menuIconFgOver - : st::menuIconFg; - p.translate(_layoutPosition); - _layout.paint(p, color->c); -} - -QPoint Widget::SpeedButton::prepareRippleStartPosition() const { - const auto innerHeight = st::mediaSpeedButton.icon.height(); - const auto result = mapFromGlobal(QCursor::pos()) - - QPoint(0, height() - innerHeight); - const auto rect = QRect(0, 0, width(), innerHeight); - return rect.contains(result) - ? result - : DisabledRippleStartPosition(); -} - -QImage Widget::SpeedButton::prepareRippleMask() const { - const auto innerHeight = st::mediaSpeedButton.icon.height(); - return Ui::RippleAnimation::RoundRectMask( - { width(), innerHeight }, - st::mediaPlayerSpeedRadius); -} - -Widget::OrderController::OrderController( - not_null button, - not_null menuParent, - Fn menuOverCallback) -: WithDropdownController(button, menuParent, std::move(menuOverCallback)) -, _button(button) { - button->setClickedCallback([=] { - showMenu(); - }); - - Core::App().settings().playerOrderModeValue( - ) | rpl::start_with_next([=] { - updateIcon(); - }, button->lifetime()); -} - -void Widget::OrderController::fillMenu(not_null menu) { - const auto addOrderAction = [&](OrderMode mode) { - struct Fields { - QString label; - const style::icon &icon; - const style::icon &activeIcon; - }; - const auto current = Core::App().settings().playerOrderMode(); - const auto active = (current == mode); - const auto callback = [=] { - Core::App().settings().setPlayerOrderMode(active - ? OrderMode::Default - : mode); - Core::App().saveSettingsDelayed(); - }; - const auto fields = [&]() -> Fields { - switch (mode) { - case OrderMode::Reverse: return { - .label = tr::lng_audio_player_reverse(tr::now), - .icon = st::mediaPlayerOrderIconReverse, - .activeIcon = st::mediaPlayerOrderIconReverseActive, - }; - case OrderMode::Shuffle: return { - .label = tr::lng_audio_player_shuffle(tr::now), - .icon = st::mediaPlayerOrderIconShuffle, - .activeIcon = st::mediaPlayerOrderIconShuffleActive, - }; - } - Unexpected("Order mode in addOrderAction."); - }(); - menu->addAction(base::make_unique_q( - menu, - (active - ? st::mediaPlayerOrderMenuActive - : st::mediaPlayerOrderMenu), - Ui::Menu::CreateAction(menu, fields.label, callback), - &(active ? fields.activeIcon : fields.icon), - &(active ? fields.activeIcon : fields.icon))); - }; - addOrderAction(OrderMode::Reverse); - addOrderAction(OrderMode::Shuffle); -} - -void Widget::OrderController::updateIcon() { - switch (Core::App().settings().playerOrderMode()) { - case OrderMode::Default: - _button->setIconOverride( - &st::mediaPlayerReverseDisabledIcon, - &st::mediaPlayerReverseDisabledIconOver); - _button->setRippleColorOverride( - &st::mediaPlayerRepeatDisabledRippleBg); - break; - case OrderMode::Reverse: - _button->setIconOverride(&st::mediaPlayerReverseIcon); - _button->setRippleColorOverride(nullptr); - break; - case OrderMode::Shuffle: - _button->setIconOverride(&st::mediaPlayerShuffleIcon); - _button->setRippleColorOverride(nullptr); - break; - } -} - -Widget::SpeedController::SpeedController( - not_null button, - not_null menuParent, - Fn menuOverCallback) -: WithDropdownController(button, menuParent, std::move(menuOverCallback)) { - button->setClickedCallback([=] { - toggleDefault(); - save(); - if (const auto current = menu()) { - current->otherEnter(); - } - }); - - setSpeed(Core::App().settings().voicePlaybackSpeed()); - _speed = Core::App().settings().voicePlaybackSpeed(true); - - button->setSpeed(_speed, anim::type::instant); - - _speedChanged.events_starting_with( - speed() - ) | rpl::start_with_next([=](float64 speed) { - button->setSpeed(speed); - }, button->lifetime()); -} - -rpl::producer<> Widget::SpeedController::saved() const { - return _saved.events(); -} - -float64 Widget::SpeedController::speed() const { - return _isDefault ? 1. : _speed; -} - -bool Widget::SpeedController::isDefault() const { - return _isDefault; -} - -float64 Widget::SpeedController::lastNonDefaultSpeed() const { - return _speed; -} - -void Widget::SpeedController::toggleDefault() { - _isDefault = !_isDefault; - _speedChanged.fire(speed()); -} - -void Widget::SpeedController::setSpeed(float64 newSpeed) { - if (!(_isDefault = (newSpeed == 1.))) { - _speed = newSpeed; - } - _speedChanged.fire(speed()); -} - -void Widget::SpeedController::save() { - Core::App().settings().setVoicePlaybackSpeed(speed()); - Core::App().saveSettingsDelayed(); - _saved.fire({}); -} - -void Widget::SpeedController::fillMenu(not_null menu) { - FillSpeedMenu( - menu->menu(), - st::mediaSpeedMenu, - _speedChanged.events_starting_with(speed()), - [=](float64 speed) { setSpeed(speed); save(); }); -} - Widget::Widget( QWidget *parent, not_null dropdownsParent, @@ -433,7 +56,7 @@ Widget::Widget( , _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle) , _repeatToggle(rightControls(), st::mediaPlayerRepeatButton) , _orderToggle(rightControls(), st::mediaPlayerRepeatButton) -, _speedToggle(rightControls()) +, _speedToggle(rightControls(), st::mediaPlayerSpeedButton) , _close(this, st::mediaPlayerClose) , _shadow(this) , _playbackSlider(this, st::mediaPlayerPlayback) @@ -443,12 +66,16 @@ Widget::Widget( std::make_unique( _orderToggle.data(), dropdownsParent, - [=](bool over) { markOver(over); })) + [=](bool over) { markOver(over); }, + Core::App().settings().playerOrderModeValue(), + [=](OrderMode value) { saveOrder(value); })) , _speedController( std::make_unique( _speedToggle.data(), dropdownsParent, - [=](bool over) { markOver(over); })) { + [=](bool over) { markOver(over); }, + [=](bool lastNonDefault) { return speedLookup(lastNonDefault); }, + [=](float64 speed) { saveSpeed(speed); })) { setAttribute(Qt::WA_OpaquePaintEvent); setMouseTracking(true); resize(width(), st::mediaPlayerHeight + st::lineWidth); @@ -789,6 +416,20 @@ void Widget::markOver(bool over) { } } +void Widget::saveOrder(OrderMode mode) { + Core::App().settings().setPlayerOrderMode(mode); + Core::App().saveSettingsDelayed(); +} + +float64 Widget::speedLookup(bool lastNonDefault) const { + return Core::App().settings().voicePlaybackSpeed(lastNonDefault); +} + +void Widget::saveSpeed(float64 speed) { + Core::App().settings().setVoicePlaybackSpeed(speed); + Core::App().saveSettingsDelayed(); +} + void Widget::mouseMoveEvent(QMouseEvent *e) { updateOverLabelsState(e->pos()); } diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index e38e8ecc6..65c179d9e 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -23,6 +23,10 @@ template class FadeWrap; } // namespace Ui +namespace Media { +enum class OrderMode; +} // namespace Media + namespace Media::View { class PlaybackProgress; } // namespace Media::View @@ -34,6 +38,9 @@ class SessionController; namespace Media::Player { class Dropdown; +class SpeedButton; +class OrderController; +class SpeedController; struct TrackState; class Widget final : public Ui::RpWidget { @@ -103,6 +110,10 @@ private: void updateTimeLabel(); void markOver(bool over); + void saveOrder(OrderMode mode); + [[nodiscard]] float64 speedLookup(bool lastNonDefault) const; + void saveSpeed(float64 speed); + const not_null _controller; const not_null _orderMenuParent; @@ -128,9 +139,6 @@ private: bool _wontBeOver = false; bool _volumeHidden = false; - class SpeedButton; - class OrderController; - class SpeedController; object_ptr _nameLabel; object_ptr> _rightControls; object_ptr _timeLabel; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index a5293cfea..e2ca66cc1 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_audio_track.h" #include "media/streaming/media_streaming_video_track.h" #include "media/audio/media_audio.h" // for SupportsSpeedControl() +#include "media/media_common.h" #include "data/data_document.h" // for DocumentData::duration() namespace Media { @@ -528,8 +529,7 @@ void Player::fail(Error error) { } void Player::play(const PlaybackOptions &options) { - Expects(options.speed >= Audio::kSpeedMin - && options.speed <= Audio::kSpeedMax); + Expects(options.speed >= kSpeedMin && options.speed <= kSpeedMax); // Looping video with audio is not supported for now. Expects(!options.loop || (options.mode != Mode::Both)); @@ -829,7 +829,7 @@ float64 Player::speed() const { } void Player::setSpeed(float64 speed) { - Expects(speed >= Audio::kSpeedMin && speed <= Audio::kSpeedMax); + Expects(speed >= kSpeedMin && speed <= kSpeedMax); if (!Media::Audio::SupportsSpeedControl()) { speed = 1.; diff --git a/Telegram/SourceFiles/media/system_media_controls_manager.cpp b/Telegram/SourceFiles/media/system_media_controls_manager.cpp index a09ad828f..fc6105ab3 100644 --- a/Telegram/SourceFiles/media/system_media_controls_manager.cpp +++ b/Telegram/SourceFiles/media/system_media_controls_manager.cpp @@ -26,8 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media { namespace { -[[nodiscard]] auto RepeatModeToLoopStatus(Media::Player::RepeatMode mode) { - using Mode = Media::Player::RepeatMode; +[[nodiscard]] auto RepeatModeToLoopStatus(Media::RepeatMode mode) { + using Mode = Media::RepeatMode; using Status = base::Platform::SystemMediaControls::LoopStatus; switch (mode) { case Mode::None: return Status::None; @@ -199,8 +199,8 @@ SystemMediaControlsManager::SystemMediaControlsManager() _controls->setIsPreviousEnabled(mediaPlayer->previousAvailable(type)); }, _lifetime); - using Media::Player::RepeatMode; - using Media::Player::OrderMode; + using Media::RepeatMode; + using Media::OrderMode; Core::App().settings().playerRepeatModeValue( ) | rpl::start_with_next([=](RepeatMode mode) { diff --git a/Telegram/SourceFiles/media/system_media_controls_manager.h b/Telegram/SourceFiles/media/system_media_controls_manager.h index 7b56d21a9..f7740771e 100644 --- a/Telegram/SourceFiles/media/system_media_controls_manager.h +++ b/Telegram/SourceFiles/media/system_media_controls_manager.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_audio_msg_id.h" #include "media/player/media_player_instance.h" +#include "media/media_common.h" namespace base::Platform { class SystemMediaControls; @@ -39,9 +40,9 @@ private: const std::unique_ptr _controls; std::vector> _cachedMediaView; - std::unique_ptr _streamed; + std::unique_ptr _streamed; AudioMsgId _lastAudioMsgId; - Media::Player::OrderMode _lastOrderMode; + OrderMode _lastOrderMode = OrderMode::Default; rpl::lifetime _lifetimeDownload; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 7c8b88e0d..d2b3decf5 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL using "ui/basic.style"; using "ui/widgets/widgets.style"; using "ui/menu_icons.style"; +using "media/player/media_player.style"; mediaviewOverDuration: 150; @@ -24,7 +25,8 @@ mediaviewPlayback: MediaSlider { seekSize: size(12px, 12px); duration: mediaviewOverDuration; } -mediaviewPlaybackTop: 52px; +mediaviewPlaybackTop: 49px; +mediaviewPlayProgressTop: 46px; mediaviewControlsButton: IconButton { ripple: RippleAnimation(defaultRippleAnimation) { @@ -34,46 +36,41 @@ mediaviewControlsButton: IconButton { duration: mediaviewOverDuration; } -mediaviewControllerSize: size(481px, 75px); +mediaviewControllerSize: size(480px, 72px); mediaviewPlayProgressLabel: LabelSimple(defaultLabelSimple) { - font: semiboldFont; + font: font(12px semibold); textFg: mediaviewPlaybackProgressFg; } -mediaviewPlayProgressSkip: 8px; -mediaviewPlayProgressLeft: 8px; -mediaviewPlayButtonTop: 5px; +mediaviewPlayProgressSkip: 10px; +mediaviewPlayProgressLeft: 4px; +mediaviewPlayButtonTop: 2px; mediaviewPlayButton: IconButton(mediaviewControlsButton) { - width: 42px; - height: 42px; - rippleAreaSize: 42px; + width: 40px; + height: 40px; + rippleAreaSize: 40px; icon: icon {{ "player/player_play", mediaviewPlaybackIconFg }}; iconOver: icon {{ "player/player_play", mediaviewPlaybackIconFgOver }}; - iconPosition: point(9px, 9px); + iconPosition: point(8px, 8px); } mediaviewPauseIcon: icon {{ "player/player_pause", mediaviewPlaybackIconFg }}; mediaviewPauseIconOver: icon {{ "player/player_pause", mediaviewPlaybackIconFgOver }}; -mediaviewButtonsTop: 7px; +mediaviewButtonsTop: 6px; +mediaviewButtonsRight: 8px; -mediaviewMenuToggleSkip: 4px; -mediaviewMenuToggle: IconButton(mediaviewControlsButton) { - width: 34px; - height: 34px; - rippleAreaSize: 34px; - icon: icon {{ "player/player_more", mediaviewPlaybackIconFg }}; - iconOver: icon {{ "player/player_more", mediaviewPlaybackIconFgOver }}; - iconPosition: point(5px, 5px); -} - -mediaviewPipButtonSkip: 5px; -mediaviewPipButton: IconButton(mediaviewMenuToggle) { +mediaviewPipButtonSkip: 4px; +mediaviewPipButton: IconButton(mediaviewControlsButton) { + width: 32px; + height: 32px; + rippleAreaSize: 32px; icon: icon {{ "player/player_pip", mediaviewPlaybackIconFg }}; iconOver: icon {{ "player/player_pip", mediaviewPlaybackIconFgOver }}; + iconPosition: point(4px, 4px); } -mediaviewFullScreenButtonSkip: 8px; -mediaviewFullScreenButton: IconButton(mediaviewMenuToggle) { +mediaviewFullScreenButtonSkip: 4px; +mediaviewFullScreenButton: IconButton(mediaviewPipButton) { icon: icon {{ "player/player_fullscreen", mediaviewPlaybackIconFg }}; iconOver: icon {{ "player/player_fullscreen", mediaviewPlaybackIconFgOver }}; } @@ -89,17 +86,17 @@ mediaviewVolumeIcon1: icon {{ "player/player_volume_small", mediaviewPlaybackIco mediaviewVolumeIcon1Over: icon {{ "player/player_volume_small", mediaviewPlaybackIconFgOver }}; mediaviewVolumeIcon2: icon {{ "player/player_volume_on", mediaviewPlaybackIconFg }}; mediaviewVolumeIcon2Over: icon {{ "player/player_volume_on", mediaviewPlaybackIconFgOver }}; -mediaviewVolumeTop: 10px; -mediaviewVolumeToggleSkip: 11px; +mediaviewVolumeTop: 6px; +mediaviewVolumeToggleLeft: 6px; mediaviewVolumeToggle: IconButton(mediaviewControlsButton) { - width: 30px; - height: 30px; - rippleAreaSize: 30px; + width: 32px; + height: 32px; + rippleAreaSize: 32px; icon: mediaviewVolumeIcon0; iconOver: mediaviewVolumeIcon0Over; - iconPosition: point(3px, 3px); + iconPosition: point(4px, 4px); } -mediaviewVolumeSkip: 4px; +mediaviewVolumeSkip: 3px; mediaviewLeft: icon { { "mediaview/next_shadow-flip_horizontal", windowShadowFg } @@ -143,6 +140,9 @@ mediaviewFileIconSize: 80px; mediaviewFileLink: defaultLinkButton; +mediaviewMenuSeparator: MenuSeparator(defaultMenuSeparator) { + fg: mediaviewMenuFg; +} mediaviewMenu: Menu(menuWithIcons) { itemBg: mediaviewMenuBg; itemBgOver: mediaviewMenuBgOver; @@ -153,9 +153,7 @@ mediaviewMenu: Menu(menuWithIcons) { itemFgShortcutOver: mediaviewMenuFg; itemFgShortcutDisabled: mediaviewMenuFg; - separator: MenuSeparator(defaultMenuSeparator) { - fg: mediaviewMenuFg; - } + separator: mediaviewMenuSeparator; ripple: RippleAnimation(defaultRippleAnimation) { color: mediaviewMenuBgRipple; @@ -183,39 +181,6 @@ mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) { } } -mediaviewControlsMenu: Menu(defaultMenu) { - itemBg: mediaviewSaveMsgBg; - itemBgOver: mediaviewPlaybackIconRipple; - itemFg: mediaviewPlaybackProgressFg; - itemFgOver: mediaviewPlaybackProgressFg; - itemFgDisabled: mediaviewPlaybackProgressFg; - itemFgShortcut: mediaviewPlaybackProgressFg; - itemFgShortcutOver: mediaviewPlaybackProgressFg; - itemFgShortcutDisabled: mediaviewPlaybackProgressFg; - - separator: MenuSeparator(defaultMenuSeparator) { - fg: mediaviewPlaybackIconRipple; - } - - arrow: icon {{ "menu/submenu_arrow", mediaviewPlaybackProgressFg }}; - - ripple: RippleAnimation(defaultRippleAnimation) { - color: mediaviewPlaybackIconRipple; - } -} -mediaviewControlsMenuShadow: Shadow(defaultEmptyShadow) { - fallback: mediaviewSaveMsgBg; -} -mediaviewControlsPanelAnimation: PanelAnimation(defaultPanelAnimation) { - fadeBg: mediaviewSaveMsgBg; - shadow: mediaviewControlsMenuShadow; -} -mediaviewControlsPopupMenu: PopupMenu(defaultPopupMenu) { - shadow: mediaviewControlsMenuShadow; - menu: mediaviewControlsMenu; - animation: mediaviewControlsPanelAnimation; -} - mediaviewSaveMsgCheck: icon {{ "mediaview/save_check", mediaviewSaveMsgFg }}; mediaviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); mediaviewSaveMsgCheckPos: point(23px, 21px); @@ -338,6 +303,59 @@ mediaviewTitleMaximizeMacPadding: margins(0px, 4px, 8px, 4px); mediaviewShadowTop: icon{{ "mediaview/shadow_top", windowShadowFg }}; mediaviewShadowBottom: icon{{ "mediaview/shadow_bottom", windowShadowFg }}; +mediaviewSpeedMenu: MediaSpeedMenu(mediaPlayerSpeedMenu) { + dropdown: DropdownMenu(mediaviewDropdownMenu) { + menu: Menu(mediaviewMenu) { + separator: MenuSeparator(mediaviewMenuSeparator) { + fg: mediaviewMenuBgOver; + padding: margins(0px, 4px, 0px, 4px); + width: 6px; + } + itemPadding: margins(54px, 7px, 54px, 9px); + itemFgDisabled: mediaviewTextLinkFg; + } + } + activeCheck: icon {{ "player/player_check", mediaviewTextLinkFg }}; + slider: MediaSlider(defaultContinuousSlider) { + activeFg: mediaviewTextLinkFg; + inactiveFg: mediaviewMenuBgOver; + activeFgOver: mediaviewTextLinkFg; + inactiveFgOver: mediaviewMenuBgOver; + activeFgDisabled: mediaviewMenuBgOver; + receivedTillFg: mediaviewMenuBgOver; + width: 6px; + seekSize: size(6px, 6px); + } + + slow: mediaSpeedSlow; + slowActive: mediaSpeedSlowActive; + normal: mediaSpeedNormal; + normalActive: mediaSpeedNormalActive; + medium: mediaSpeedMedium; + mediumActive: mediaSpeedMediumActive; + fast: mediaSpeedFast; + fastActive: mediaSpeedFastActive; + veryFast: mediaSpeedVeryFast; + veryFastActive: mediaSpeedVeryFastActive; + superFast: mediaSpeedSuperFast; + superFastActive: mediaSpeedSuperFastActive; +} +mediaviewSpeedButton: MediaSpeedButton(mediaPlayerSpeedButton) { + size: size(32px, 32px); + padding: margins(0px, 0px, 0px, 0px); + fg: mediaviewPlaybackIconFg; + overFg: mediaviewPlaybackIconFgOver; + activeFg: mediaviewTextLinkFg; + icon: icon{{ "player/player_speed", mediaviewPlaybackIconFg }}; + ripple: RippleAnimation(defaultRippleAnimation) { + color: mediaviewPlaybackIconRipple; + } + rippleActiveColor: mediaviewPlaybackIconRipple; + rippleRadius: 16px; + menu: mediaviewSpeedMenu; + menuAlign: align(bottomright); +} + themePreviewSize: size(903px, 584px); themePreviewBg: windowBg; themePreviewOverlayOpacity: 0.8; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 339e5332d..905fd1c1b 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -3743,10 +3743,8 @@ void OverlayWidget::playbackControlsSpeedChanged(float64 speed) { } } -float64 OverlayWidget::playbackControlsCurrentSpeed() { - const auto result = Core::App().settings().videoPlaybackSpeed(); - DEBUG_LOG(("Media playback speed: now %1.").arg(result)); - return result; +float64 OverlayWidget::playbackControlsCurrentSpeed(bool lastNonDefault) { + return Core::App().settings().videoPlaybackSpeed(lastNonDefault); } void OverlayWidget::switchToPip() { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 7b9dd5788..a70b64f79 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -204,7 +204,7 @@ private: void playbackControlsVolumeToggled() override; void playbackControlsVolumeChangeFinished() override; void playbackControlsSpeedChanged(float64 speed) override; - float64 playbackControlsCurrentSpeed() override; + float64 playbackControlsCurrentSpeed(bool lastNonDefault) override; void playbackControlsToFullScreen() override; void playbackControlsFromFullScreen() override; void playbackControlsToPictureInPicture() override; diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 3b18ba9a6..8ab0eb549 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -8,13 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/view/media_view_playback_controls.h" #include "media/audio/media_audio.h" +#include "media/player/media_player_button.h" +#include "media/player/media_player_dropdown.h" #include "media/view/media_view_playback_progress.h" #include "ui/widgets/labels.h" #include "ui/widgets/continuous_sliders.h" #include "ui/effects/fade_animation.h" #include "ui/widgets/buttons.h" #include "ui/widgets/menu/menu_item_base.h" -#include "ui/widgets/popup_menu.h" #include "ui/text/format_values.h" #include "ui/cached_round_corners.h" #include "lang/lang_keys.h" @@ -22,184 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media { namespace View { -namespace { - -constexpr auto kMinSpeed = 50; -constexpr auto kMaxSpeed = 200; - -constexpr float64 SpeedShiftToValue(float64 value) { - const auto valueAsSpeedF = value * 100.; - const auto valueAsSpeed = int(valueAsSpeedF + 0.5); // constexpr round. - return float64(valueAsSpeed) / (kMaxSpeed - kMinSpeed); -}; - -constexpr float64 SpeedToValue(float64 value) { - const auto valueAsSpeedF = value * 100.; - const auto valueAsSpeed = int(valueAsSpeedF + 0.5); // constexpr round. - return float64(valueAsSpeed - kMinSpeed) / (kMaxSpeed - kMinSpeed); -}; - -constexpr auto kSpeedStickedValues = - std::array, 5>{{ - { SpeedToValue(0.75), SpeedShiftToValue(0.03) }, - { SpeedToValue(1.00), SpeedShiftToValue(0.05) }, - { SpeedToValue(1.25), SpeedShiftToValue(0.03) }, - { SpeedToValue(1.50), SpeedShiftToValue(0.03) }, - { SpeedToValue(1.75), SpeedShiftToValue(0.03) }, - }}; - -class MenuSpeedItem final : public Ui::Menu::ItemBase { -public: - MenuSpeedItem( - not_null parent, - const style::Menu &st, - float64 startSpeed); - - not_null action() const override; - bool isEnabled() const override; - - [[nodiscard]] rpl::producer changeSpeedRequests() const; - -protected: - int contentHeight() const override; - -private: - float64 computeSpeed(float64 value) const; - QString speedString(float64 value) const; - - QRect _itemRect; - QRect _textRect; - - const style::MediaSlider &_sliderSt; - const base::unique_qptr _slider; - const not_null _dummyAction; - const int _lineCount; - const int _height; - - rpl::event_stream _changeSpeedRequests; - -}; - -MenuSpeedItem::MenuSpeedItem( - not_null parent, - const style::Menu &st, - float64 startSpeed) -: Ui::Menu::ItemBase(parent, st) -, _sliderSt(st::mediaviewPlayback) -, _slider(base::make_unique_q( - this, - _sliderSt)) -, _dummyAction(new QAction(parent)) -, _lineCount(std::ceil(st.itemStyle.font->width(speedString(0.9)) - / float(st.widthMax))) -, _height(st.itemPadding.top() * 2 - + st.itemStyle.font->height * _lineCount - + _sliderSt.seekSize.height() - + st.itemPadding.bottom() * 2) { - - initResizeHook(parent->sizeValue()); - enableMouseSelecting(); - enableMouseSelecting(_slider.get()); - - _slider->setAlwaysDisplayMarker(true); - _slider->setValue((base::SafeRound(startSpeed * 100.) - kMinSpeed) - / (kMaxSpeed - kMinSpeed)); - - for (const auto &sticked : kSpeedStickedValues) { - _slider->addDivider(sticked.first, st::speedSliderDividerSize); - } - //_slider->addDivider( - // kSpeedStickedValues[1].first, - // st::speedSliderDividerSize); - - { - const auto goodWidth = st.itemPadding.left() - + st.itemPadding.right() - + st.itemStyle.font->width(speedString(0.9)); - setMinWidth(std::clamp(goodWidth, st.widthMin, st.widthMax)); - } - - sizeValue( - ) | rpl::start_with_next([=](const QSize &size) { - const auto geometry = QRect(QPoint(), size); - _itemRect = geometry - st.itemPadding; - - const auto height = _itemRect.height(); - const auto penWidth = QPen(st.itemBgOver).width(); - _textRect = _itemRect - - style::margins( - -penWidth, - 0, - -penWidth, - height - st.itemStyle.font->height * _lineCount); - - const auto sliderGeometry = _itemRect - - style::margins(0, height - _sliderSt.seekSize.height(), 0, 0); - _slider->setGeometry(sliderGeometry); - }, lifetime()); - - paintRequest( - ) | rpl::start_with_next([=](const QRect &clip) { - auto p = QPainter(this); - - const auto selected = isSelected(); - p.fillRect(clip, selected ? st.itemBgOver : st.itemBg); - - const auto value = _slider->value(); - - p.setPen(selected ? st.itemFgOver : st.itemFg); - p.setFont(st.itemStyle.font); - p.drawText(_textRect, speedString(value), style::al_left); - }, lifetime()); - - _slider->setChangeProgressCallback([=](float64 value) { - update(_textRect); - }); - - _slider->setChangeFinishedCallback([=](float64 value) { - _changeSpeedRequests.fire_copy(computeSpeed(value)); - }); - - _slider->setAdjustCallback([=](float64 value) { - for (const auto &snap : kSpeedStickedValues) { - if (value > (snap.first - snap.second) - && value < (snap.first + snap.second)) { - return snap.first; - } - } - return value; - }); -} - -float64 MenuSpeedItem::computeSpeed(float64 value) const { - return anim::interpolate(kMinSpeed, kMaxSpeed, std::clamp(value, 0., 1.)) - / 100.; -} - -QString MenuSpeedItem::speedString(float64 value) const { - return tr::lng_mediaview_playback_speed( - tr::now, - lt_speed, - QString::number(computeSpeed(value), 'f', 2) + 'x'); -} - -not_null MenuSpeedItem::action() const { - return _dummyAction; -} - -bool MenuSpeedItem::isEnabled() const { - return true; -} - -int MenuSpeedItem::contentHeight() const { - return _height; -} - -rpl::producer MenuSpeedItem::changeSpeedRequests() const { - return _changeSpeedRequests.events(); -} - -} // namespace PlaybackControls::PlaybackControls( QWidget *parent, @@ -211,12 +34,18 @@ PlaybackControls::PlaybackControls( , _playbackProgress(std::make_unique()) , _volumeToggle(this, st::mediaviewVolumeToggle) , _volumeController(this, st::mediaviewPlayback) -, _menuToggle(this, st::mediaviewMenuToggle) +, _speedToggle(this, st::mediaviewSpeedButton) , _fullScreenToggle(this, st::mediaviewFullScreenButton) , _pictureInPicture(this, st::mediaviewPipButton) , _playedAlready(this, st::mediaviewPlayProgressLabel) , _toPlayLeft(this, st::mediaviewPlayProgressLabel) -, _menuStyle(st::mediaviewControlsPopupMenu) +, _speedController( + std::make_unique( + _speedToggle.data(), + parent, + [=](bool) {}, + [=](bool lastNonDefault) { return speedLookup(lastNonDefault); }, + [=](float64 speed) { saveSpeed(speed); })) , _fadeAnimation(std::make_unique(this)) { _fadeAnimation->show(); _fadeAnimation->setFinishedCallback([=] { @@ -229,9 +58,6 @@ PlaybackControls::PlaybackControls( _pictureInPicture->addClickHandler([=] { _delegate->playbackControlsToPictureInPicture(); }); - _menuToggle->addClickHandler([=] { - showMenu(); - }); _volumeController->setValue(_delegate->playbackControlsCurrentVolume()); _volumeController->setChangeProgressCallback([=](float64 value) { @@ -354,33 +180,13 @@ void PlaybackControls::fadeUpdated(float64 opacity) { _volumeController->setFadeOpacity(opacity); } -void PlaybackControls::showMenu() { - if (_menu) { - _menu = nullptr; - return; - } - _menu.emplace(this, _menuStyle); +float64 PlaybackControls::speedLookup(bool lastNonDefault) const { + return _delegate->playbackControlsCurrentSpeed(lastNonDefault); +} - if (Media::Audio::SupportsSpeedControl()) { - auto speedItem = base::make_unique_q( - _menu, - _menuStyle.menu, - _delegate->playbackControlsCurrentSpeed()); - speedItem->changeSpeedRequests( - ) | rpl::start_with_next([=](float64 speed) { - updatePlaybackSpeed(speed); - }, speedItem->lifetime()); - _menu->addAction(std::move(speedItem)); - _menu->addSeparator(); - } - - _menu->addAction(tr::lng_mediaview_rotate_video(tr::now), [=] { - _delegate->playbackControlsRotate(); - }); - - _menu->setForcedOrigin(Ui::PanelAnimation::Origin::BottomLeft); - _menu->popup(mapToGlobal(_menuToggle->geometry().topLeft())); +void PlaybackControls::saveSpeed(float64 speed) { + _delegate->playbackControlsSpeedChanged(speed); } void PlaybackControls::updatePlaybackSpeed(float64 speed) { @@ -524,7 +330,7 @@ void PlaybackControls::setInFullScreen(bool inFullScreen) { void PlaybackControls::resizeEvent(QResizeEvent *e) { const auto textSkip = st::mediaviewPlayProgressSkip; const auto textLeft = st::mediaviewPlayProgressLeft; - const auto textTop = st::mediaviewPlaybackTop + (_playbackSlider->height() - _playedAlready->height()) / 2; + const auto textTop = st::mediaviewPlayProgressTop; _playedAlready->moveToLeft(textLeft + textSkip, textTop); _toPlayLeft->moveToRight(textLeft + textSkip, textTop); const auto remove = 2 * textLeft + 4 * textSkip + _playedAlready->width() + _toPlayLeft->width(); @@ -536,16 +342,16 @@ void PlaybackControls::resizeEvent(QResizeEvent *e) { (width() - _playPauseResume->width()) / 2, st::mediaviewPlayButtonTop); - auto right = st::mediaviewMenuToggleSkip; - _menuToggle->moveToRight(right, st::mediaviewButtonsTop); - right += _menuToggle->width() + st::mediaviewPipButtonSkip; + auto right = st::mediaviewButtonsRight; + _speedToggle->moveToRight(right, st::mediaviewButtonsTop); + right += _speedToggle->width() + st::mediaviewPipButtonSkip; _pictureInPicture->moveToRight(right, st::mediaviewButtonsTop); right += _pictureInPicture->width() + st::mediaviewFullScreenButtonSkip; _fullScreenToggle->moveToRight(right, st::mediaviewButtonsTop); updateDownloadProgressPosition(); - auto left = st::mediaviewVolumeToggleSkip; + auto left = st::mediaviewVolumeToggleLeft; _volumeToggle->moveToLeft(left, st::mediaviewVolumeTop); left += _volumeToggle->width() + st::mediaviewVolumeSkip; _volumeController->resize( @@ -586,7 +392,7 @@ void PlaybackControls::mousePressEvent(QMouseEvent *e) { } bool PlaybackControls::hasMenu() const { - return _menu != nullptr; + return _speedController->menu() != nullptr; } bool PlaybackControls::dragging() const { @@ -594,7 +400,7 @@ bool PlaybackControls::dragging() const { || _playbackSlider->isChanging() || _playPauseResume->isOver() || _volumeToggle->isOver() - || _menuToggle->isOver() + || _speedToggle->isOver() || _fullScreenToggle->isOver() || _pictureInPicture->isOver(); } diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.h b/Telegram/SourceFiles/media/view/media_view_playback_controls.h index b972d217a..5f79cba52 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.h @@ -23,6 +23,8 @@ class PopupMenu; namespace Media { namespace Player { struct TrackState; +class SpeedButton; +class SpeedController; } // namespace Player namespace View { @@ -42,7 +44,8 @@ public: virtual void playbackControlsVolumeToggled() = 0; virtual void playbackControlsVolumeChangeFinished() = 0; virtual void playbackControlsSpeedChanged(float64 speed) = 0; - [[nodiscard]] virtual float64 playbackControlsCurrentSpeed() = 0; + [[nodiscard]] virtual float64 playbackControlsCurrentSpeed( + bool lastNonDefault) = 0; virtual void playbackControlsToFullScreen() = 0; virtual void playbackControlsFromFullScreen() = 0; virtual void playbackControlsToPictureInPicture() = 0; @@ -50,6 +53,7 @@ public: }; PlaybackControls(QWidget *parent, not_null delegate); + ~PlaybackControls(); void showAnimated(); void hideAnimated(); @@ -60,8 +64,6 @@ public: [[nodiscard]] bool hasMenu() const; [[nodiscard]] bool dragging() const; - ~PlaybackControls(); - protected: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; @@ -86,9 +88,11 @@ private: void updatePlayPauseResumeState(const Player::TrackState &state); void updateTimeTexts(const Player::TrackState &state); void refreshTimeTexts(); - void showMenu(); - not_null _delegate; + [[nodiscard]] float64 speedLookup(bool lastNonDefault) const; + void saveSpeed(float64 speed); + + const not_null _delegate; bool _inFullScreen = false; bool _showPause = false; @@ -106,15 +110,13 @@ private: std::unique_ptr _receivedTillProgress; object_ptr _volumeToggle; object_ptr _volumeController; - object_ptr _menuToggle; + object_ptr _speedToggle; object_ptr _fullScreenToggle; object_ptr _pictureInPicture; object_ptr _playedAlready; object_ptr _toPlayLeft; object_ptr _downloadProgress = { nullptr }; - - const style::PopupMenu &_menuStyle; - base::unique_qptr _menu; + std::unique_ptr _speedController; std::unique_ptr _fadeAnimation; }; diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index 1657d175d..82a9c70e6 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL using "ui/basic.style"; using "ui/chat/chat.style"; using "ui/widgets/widgets.style"; -using "media/view/media_view.style"; using "boxes/boxes.style"; OverviewFileLayout { diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style index 999df51ad..7be43b08f 100644 --- a/Telegram/SourceFiles/ui/menu_icons.style +++ b/Telegram/SourceFiles/ui/menu_icons.style @@ -131,3 +131,29 @@ menuIconDisableAttention: icon {{ "menu/disable", menuIconAttentionColor }}; menuIconReportAttention: icon {{ "menu/report", menuIconAttentionColor }}; menuIconBlockSettings: icon {{ "menu/block", windowBgActive }}; + +playerSpeedSlow: icon {{ "player/speed/audiospeed_menu_0.5", menuIconColor }}; +playerSpeedSlowActive: icon {{ "player/speed/audiospeed_menu_0.5", mediaPlayerActiveFg }}; +playerSpeedNormal: icon {{ "player/speed/audiospeed_menu_1.0", menuIconColor }}; +playerSpeedNormalActive: icon {{ "player/speed/audiospeed_menu_1.0", mediaPlayerActiveFg }}; +playerSpeedMedium: icon {{ "player/speed/audiospeed_menu_1.2", menuIconColor }}; +playerSpeedMediumActive: icon {{ "player/speed/audiospeed_menu_1.2", mediaPlayerActiveFg }}; +playerSpeedFast: icon {{ "player/speed/audiospeed_menu_1.5", menuIconColor }}; +playerSpeedFastActive: icon {{ "player/speed/audiospeed_menu_1.5", mediaPlayerActiveFg }}; +playerSpeedVeryFast: icon {{ "player/speed/audiospeed_menu_1.7", menuIconColor }}; +playerSpeedVeryFastActive: icon {{ "player/speed/audiospeed_menu_1.7", mediaPlayerActiveFg }}; +playerSpeedSuperFast: icon {{ "player/speed/audiospeed_menu_2.0", menuIconColor }}; +playerSpeedSuperFastActive: icon {{ "player/speed/audiospeed_menu_2.0", mediaPlayerActiveFg }}; + +mediaSpeedSlow: icon {{ "player/speed/audiospeed_menu_0.5", mediaviewMenuFg }}; +mediaSpeedSlowActive: icon {{ "player/speed/audiospeed_menu_0.5", mediaviewTextLinkFg }}; +mediaSpeedNormal: icon {{ "player/speed/audiospeed_menu_1.0", mediaviewMenuFg }}; +mediaSpeedNormalActive: icon {{ "player/speed/audiospeed_menu_1.0", mediaviewTextLinkFg }}; +mediaSpeedMedium: icon {{ "player/speed/audiospeed_menu_1.2", mediaviewMenuFg }}; +mediaSpeedMediumActive: icon {{ "player/speed/audiospeed_menu_1.2", mediaviewTextLinkFg }}; +mediaSpeedFast: icon {{ "player/speed/audiospeed_menu_1.5", mediaviewMenuFg }}; +mediaSpeedFastActive: icon {{ "player/speed/audiospeed_menu_1.5", mediaviewTextLinkFg }}; +mediaSpeedVeryFast: icon {{ "player/speed/audiospeed_menu_1.7", mediaviewMenuFg }}; +mediaSpeedVeryFastActive: icon {{ "player/speed/audiospeed_menu_1.7", mediaviewTextLinkFg }}; +mediaSpeedSuperFast: icon {{ "player/speed/audiospeed_menu_2.0", mediaviewMenuFg }}; +mediaSpeedSuperFastActive: icon {{ "player/speed/audiospeed_menu_2.0", mediaviewTextLinkFg }}; diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index fea8a0e0c..be9ad16a0 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -125,6 +125,8 @@ PRIVATE media/player/media_player_dropdown.cpp media/player/media_player_dropdown.h + media/media_common.h + menu/menu_check_item.cpp menu/menu_check_item.h menu/menu_ttl.cpp diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 62a62d1fb..dec1cd8ce 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 62a62d1fb5abe9dc1e6b9f89af1ccef6fef33c52 +Subproject commit dec1cd8cea24e396c37c327929c0135d46541626