From ff657347b8cc5979208d648e461edc5218885771 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 9 Sep 2016 18:52:46 +0300 Subject: [PATCH] Featured stickers section done in StickersPanel. EmojiPan moved to a separate module stickers/emoji_pan. FFmpeg linked by msvs linker flags in GYP to use ".a" extension. --- Telegram/Resources/art/sprite.png | Bin 180375 -> 181502 bytes Telegram/Resources/art/sprite_200x.png | Bin 244010 -> 246441 bytes Telegram/Resources/basic.style | 2 + Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/application.cpp | 12 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 122 +- Telegram/SourceFiles/dropdown.cpp | 3447 +--------------- Telegram/SourceFiles/dropdown.h | 599 +-- Telegram/SourceFiles/facades.h | 11 +- Telegram/SourceFiles/historywidget.cpp | 156 +- Telegram/SourceFiles/historywidget.h | 11 +- Telegram/SourceFiles/mainwidget.cpp | 1 + Telegram/SourceFiles/stickers/emoji_pan.cpp | 3704 ++++++++++++++++++ Telegram/SourceFiles/stickers/emoji_pan.h | 657 ++++ Telegram/SourceFiles/stickers/stickers.cpp | 143 + Telegram/SourceFiles/stickers/stickers.h | 29 + Telegram/SourceFiles/stickers/stickers.style | 39 + Telegram/gyp/Telegram.gyp | 5 + Telegram/gyp/telegram_win.gypi | 16 +- 19 files changed, 4703 insertions(+), 4252 deletions(-) create mode 100644 Telegram/SourceFiles/stickers/emoji_pan.cpp create mode 100644 Telegram/SourceFiles/stickers/emoji_pan.h create mode 100644 Telegram/SourceFiles/stickers/stickers.cpp create mode 100644 Telegram/SourceFiles/stickers/stickers.h create mode 100644 Telegram/SourceFiles/stickers/stickers.style diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png index 932c1d9903fca15bd7022f818909ed8c20104a1b..bb18b32eaa1586bd26aa1c5584e9d31476880fce 100644 GIT binary patch delta 56550 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 bEPxHo3V99c0Zh0O41D(6JL4+$66601c3T_G delta 55440 zcma%ibyQSQ+wU2=ySqU`YUu6~q+3FfP8FmNt$;Mr4T6fagyaAsARr}O14u~?O6PZc z-*2tE?)~eo#af&*=gjQApWM&$`|aCU{LFCtWGPrcope7sp}2sEq_ME1w2-j0uy`5~ zhSdM_i7|#8hmeTKlP4k)Z~^6i&M@Eo`HU4P~wS0Oxn+;AJo33Kut1&V%O-<$BDj|vfp@?k*e^g5i-j6k-@zqe=x6oli#~nU4YEbWQ{c1UD$0gA1I0aRlQ{dsz{jYE zDc-9;(0Q=DNfn<4o!9kO_?j&GougJU!V-Yvpwm5Cb&{ydK$t%&-^3=DG~{m&h4R}a zzxohH0m%zJEFtytG608cAQEPWiNIwBSfYqIU5xNq=nsdYWD;hj|7jV9uWr+0zT6 zrmeg}RNpaB^3D^W!yaIIGlqEsRI5+YNurG2DQ0P&p(hg|VSo(m(+j7?zhXT&7~V2r zM}RthPE<1~hFuma`<}ZDv7JoV3E1=MC^|l+X)VG?;0d(AZs3*(iTTcl z)7`h&5#^K7H9K85r}V}X`o$$vq{K*jpc(f`i9yzO>$`It<}}n1wh#NM-XBKvjnYjMfYR8%%*5eZ@x2nA|KsPF$kt@fS(%fMK#BQ+<9Z5Sxyuu4Feu(?_p z4(|-G$6M7k(hlmP`Z520=A)XhQZ3b2^!M2MTTd@cmAG(`HUK*i%<~{UBuA(RCd)JY zC+KR7Fdh@tf|A0q!wtJbb9U~1Zi#F6iH~u*#ultBEL@={OXwDGhUdI$cAuNj6d~wk zfM-7t)XfrFmB@6$TmeHnrkp>4p1Ufe3@<*9m;_ikDuj$ZDol`6ycBV1c`|hNcBSX% zjE&d3ZKlxB)|&lB;p{iW2Bw?*j3l2sIDgpx&RA#`H>WGOx|m)emY&~?l@&w~BV>Uy zn4R1hPr9X#`v=fHl?4dc^pRsbuVXB(MY{y>?;BFfs4=5n|`mua9&)u3+F!8i)vkiOzZr#pg zDks7paILGX+&tf0yRx;FZsWu%{b1#}qV4u!{1)XEtoN%afNc~X2NNX-G~+dZO_W~E z7GvU$cxfA%-5@r8qm=y^P=M*>M(pNvsQ+1!<~gh#7ujGce`~sG|IF9!r8@8S#5;Ut zW#yuAXMHQH`1*Pg3To;gek)IJCmP3vnn`B5CtkU2e_rnp2Y5^$RF0s)>G-&QH_eyYQXT_`_cTFp#xpR>UytsJX9!+m&7-l9)G; zD%jamG`Z@9avG3_iSm=-z`wVKA+U}UMsIc0VuYD`iLMx1TSX8Y+-SA^^8 zx&QgqRpe6(Rdw|evl^T2l`aL%37I5yoisI_EdO~cNhd4Pv}Yb>XD2r`g84T~3QqUM zAw7O@0LO9igo!XpGV;u?tvPo1;5(je)6=Vo< zKq&Ye1p@wzh-JB;QwIWO`D?vZI_@ua)yg$C9f~9vdU=xJ6BCA^;_t_coJI>?zkaRw z_%YkFXU}A%y=*z~7nBUNiOfo+J~rkpqPZyk1mVW@05DAIc+x0;tob_U8-snsiYwhW z3cyL4#X|FlCv4JtoOPd7d^UN|G8{3@N12?Jg_EEj+2AxPp878!Q02)6kTo_oYHDhR z>1_oM4rSFSJYfE3J{@XQ)dNQ3oh(yuS>6s6{u)_(soTcBW4+lrnxN1h<&4_s>>KIpl|q~=sChIf6t zdt|ATkU{jPz?n*WBLKIqhoC?W+?8&5t1Me1RcnF2<&kFW1l*x_i6dQm6%ZQ6O9@m2 z-AfHl#Mq(srTc!OVY%r8MJ{wy@qGuOKCH}Y26|6#q;HCa*TU`-{h8c^SCwBs4geGQAee&2D`qkCD&fv&t*8(43L^Jn8#-;`CUkr=S61`t)MhaUsk6XIFAg`a5 zT_sG23H~+(TPctgv`vJA_C02YPrq2Mu1^&!OU{HYPYZd5$NO9`(r6*6uM;S4o2cm0 zh^5*0*VZ0Gl4cVVqvU>A;GPN>;NihJIyw@{e(?U2aY3oUPlB(9K<%wpQak)$p+-Zq z8S?qe@dE0ZMsbVRsPb~wQ0y=hiE7XVGJ*d-9cp0?ZUu?Gs*qfgi4E1ZUAH{_K3S4J zk9n0yDB9Pl?W3P}Fe8G!NWaMeon`?gxYylRWS$sZF(qO}k{5P9KEfrXrKGg9vB$?R zD5$7<97I^*a0*;pToxQOryqGfQdi}Rh=W198du5!(p15)dJ!i^sTQ?~qEEvXFOyg$ zYJ)^(n?VwT*J*>ht4n=ds=B&_qQ1X>Ml$plx3ow~ad1S%oH6XEql%lffFD8HG-qh$ zRh>WloUW~+uz!tG$tLOyV#Ai}LXNi$%F4@2o^Soo)YbJ^bP~_?xjc4B&&;%?lo+&X zxX$=VWob$7q9sy-9?>L2?_kOHd0N9K-VTOpJp?1lT@tc<=7(zWwu14)1f7Y&c5SL3 zb&SC|YwSz|W4-u;*txQprhN7F^~q^zlMe4*Leu*I^zwM<*vO*cFw0h{bFUccoy7X)-ozq0%6g} zW>5q^K%y=Z#__W3C0f^ai|A(w@Csl&X#Swx1%;}PXHh#fmTJZxpf_N0n?XT;?F z_JH?evK->hJe7n@Vx8%!DY3G$vQMzEy*+SGD?B{>RzUGVH_++h`|Zm1Ex&9JA##%D zuD37L2zE{1PF%`-&T#q^883H|w#!1W-St7R?u^U2)13R!;GxlO+Z6`cnDSd$OlB7 zx9Lx@za)tg<^Nq+HaL!Ch`CLJ$0-YkSe;nR<5a-%^<_evcI23XV)9wzFs*spjg zKYK2l`GX-vjq&BMX$$+bs;Z_eI2hHzS;%x-gp7Z}>{G}PdUxHAy<&U( zJ!Y%4BY>fFyk*Kx$RB*FSkd^?8BDlqOKb|z@zWvt>7C3S$DyTnF3C=q^jB9ZKDDGf z^Y>q~{xhm>P82OGqw?!?S7U{a@D=dxMlY@;V-mi!w=6GXqMzI49vdwbQ0B%WP-`RsMVmrK589~i;n?ONfffLuj9VeVK^{%&oKwz0>;WBqRTZ4Y=16vV_B8z>?_Gv%)Z2_6E!P%%0!6 zMX1%NLT@ygLn@u+wQAz}GmDHRGaH-R)$5DD@zJ{njZT}r9r%Z3_eP#+>xc!}@H@Ii z_gPDLF2Bw^3kU@NBZ$#aug`-H^W~E}i+ba$`)t}j9CT`$8L#nJu&Sjxae+l!@WwZ% zgvWEX);`zS>=&VfeW6*#7x$3f0SKsky2#A%d?{0nVLfn z9jX~yFl7C`^5lyaXbbz1Sm9boGq!zgZH>dKe$2Hol9Pj)jD!R=-RiqbQKd@YS3~dFV!u`WsO>cJEJQpPrcZ&H%CRwR zPXY|=FEk&vU2NvQls_I*0WJ8c8uAF7OhG}wBr#MGCyaVZxTEhVd?JQItwY1W7_Mee z43z}}sCXA^bwx#l&a<58kvg)g)BQHZ*!J4mS|~rO>lhgCjYzD>8tq8oVF;$= zs5X*V0AX6^3sa6k60w61afiik?AT|14Qh4X-Cp_7C2*y!w=AitsmVEw7_1s&w4pKW{DvDn_EK*rrbM=R8ojCtWv#TE$Y*2-H!`8`0fYe z^p+coE_Io|D@~H8E1Gg0eDmfFd(hc|hdNzJuhlmOHK6GBa<`^~oLf&;-GJg`&Z+QZ z(itw@QjnL7roZ-D=yn4OAN=6G9)cnC(0;)(mwxtj=*^`r#pk5dVwG1m>#9Fgqx+5K zJ=2wY=r_Jc*Nww3aU6NQlKbp|D-Q4u!&}ezca+Bg!j65b_SbXe*-$;_Evnnebl+vr_{E=b;poJb-dyjwCu3Cd*~%s#L>?iNQ9G-1#Tw>zl7s^nJqro zXo??rwVTZ(2`oZiRyfYI4W(5~JI)FebSLX`Vt?{ChFtk2u1i+@7*3ME97tlpu`o*e$^TS5mzV@& z_2Xaqe<5S=p~B6JNJWZ}sF8x?KHvDQhs#gBwjhJw0uX!Al{J`!KQytBcXbZU4u)5qMM-+CF<5o@E2+ zY_Y}XmDfK>K>?6sa&i(|1!a=VfveSIi3a#05#b@tOMVOX!1+d|?PU^;@MD^sE5-B( zm^b*NAVye&vDb=Xq5k0yM)V}Ccu{Zbk16XFzO~{xy4*z z_^P!20M5Oh z?uH3{%X(1m!RF#W)Ga`?HvtQoO4zlnU)mIyV*-(~=QvUc*uCVU{wO4C*X^a7w3-2p zjNe}OdVdmg8Bg4%WFm`JYLQEhJfzHSpY?By95@jREk5fA``#Er%7LDxVqNyUs{pB< z7Iek)DX2&TB7omCL;+J&c{TM#Mbi=OuK0MnH2SKQ(msJ`H11wbP%$Oh?{k-)6|@PS z?9K?9{JGojQLz`JA=qVLeBar5>zna(L=Wq15pwSKs$XCE`1q)MwdNfwrz~4sCd=P3 zrS>HtL+H2>8V|qd(Ct(44|x{xxL(SgEwWdtEk7TXyQNUmb+Yh_Y)-T=GP`M=bM-xM z7c%8K`&l4IR7ic##Xpk2483hX?0z#iVpYEsMcH2SU_G3G2oQzRMmJ<+3=c>!JilyY zptrp|l>apDteK#$^3UmCd3BsjqXVejnl2(HS$~v%ciyi-S#$QhHj_jlrcOG|lJM1J zE069Ec%nSsSrc+Jg-MA$XHTA>P6!QX$hVRfU9qb2n)g{32#br0?RR!|!1$o!ZJ&a2 z7nc@?xUR0do#W%<4V4&z>|vip5mcf#FB{uh1r)dDbYtc1d!qQ``$VGcX)I(Udxa>u z(snwX3Ify(p4M(}Z~ujE)n;RXe_Mj$;?<2EWJ>EzHZoe@D2wu0SZlzhRK@TqNcn!lEUJnd$KL6if^hh!?dgHy<&b+vsBh6$$0`#I$yU~IUZ*#kA`MJ2T)^>#rW}i99 zc6Qc@-cIvjH2V>L{mI07e$-^Xn1@V?7kl7Vu-VZ>A9%bioK(JpeJ>90?XKeFF+7#S zj1(g!bfcMU=l1qC_Z0zc{oHc+KkYT1oSdN3##p%4nQKSS?Q+K0%YXRrVFy}m%Tjfo zzqdDP8iXnj7l!^SZk!UsWV4?7y|asp3!|Ri-lPC+slouB_bA5Inj^xhMZ zOgzhV&iKA-EfW)quC1C7gQLGClRMw6qa9dtz<0M8MSqVCV2IzLvH#dfhgF}ca zJ{qch3?KdT<=m3$uf0}PrubT^>KRYL zd%y;T8Yj-e2!|FEHxAp+@ougMS>9MS!-@>_^g%F$n+-`Lo}RMPVe4+SA#0#(ydqs? z(WkL=gH9SIL3BXc2%y|kUMUIjC-RyCzp}` zw&zV~4Cu@+L}+zqOup#S{ma64{mkGwOi^1~`=MY8CPKq2R50(kXb((8QfFr8X z$mOxY1cD`*ZA`F-_{o}zaFZJlqtTYRRQ7&4f=)F4wIW&-jm%V;-&yQcYj{i2-SXHN zDr?Z;l3UH-mY}!27@XX>og}sW({|9hi$P(^)xKNZrj4}3U0}9C{j^eTVyxFMwo^R& z`#aE>`Pz zRh3n9sJ!EFmZh)r_N*PvrW`Q((P8y6?7nYx5MTNX{xdunKN?D0RzkOXNENo4r@6|2 zzfbzjxAT_dbuTE_TU?tM{&qe2_D8v)zO6FoJF% z##H~HvMSd(PnQK!*C;Q^S{_Z2C5*F2swX98HJ3vPP>)gZ>7XPeh@Vx&Git?N&A1=l z@=gcn@lBseYP(P-tS2A{RNFz+xx3x%!GOYPYdENZ%Q5Z{@&IZtvPHilq)=O`nsP0T z?(Xq)O|B$__CJ1Zzl=qYyt-PRGVgVs23bB|G9{lgQwvjFt;1gZMzm2@ zU9q*-G>f#q-hMxg5y3!mqOuD#E&vydNaErUTtBu8)gBW3&d*X>Lj3yEc53ei1~ci2Oju_0^9a!%XVHIN9X%K~fyEag269|R zxe0gXGBx|+tKQ*snJQG=JvCeX6t2`ogT}wW6;+Ix3?S26R=>uQuflaD%CU-F0&QGe zb}|MA21EgUTidQjrQ_zXqW1RSeANYDLEJL+qn74?GG|lLRO+}h1M9y{ng9-YSTn*&(BHE5{Bv0ot1BfYQit!8(S#Pq zo3GQY-E^snE|HY)2nkYE;QsZtJl~+qxh)Ag->V;CMxd$4`BBi4l2hOid1%wz1Cn!c zq#ssJ%?qr59Ct2v0pU3=;tLaEbR&*wgP%8kelYs|{_!h*=NvuTFV8q7pnl0K_RBa* zK1q$T?(gr54SV!IuGaAn85pnYAm+Z;i+QCuJh_GaNX*{-Xvh1dd(jF#n7&NN0vkzk zo)_92o@6c(VXLr5sE^Ei_%qx#{p_tGU7Q7$vrR_5nqnHp{V7nmNDCPcWq$d4OovHt zdITP!g+%ZJ$eDIaYggcUP(10`E(ZY{x&YOXQheXDF;|bA>k-#R%VEdo34M=0NaT)O zr0zTZdtw1OEUn?luW#dt=5CKh$%XG$53n2pZ_&&6T zxlhZ#PUF!);0~kk&5I628YN`@I)}ARi7rmaH3qG^(xys}FrKxPutD$kBAr?B^l$_k zYSKfPhGr+;4MXZW)hsC)5HPPE!Brs!Dka=UlCxPC(U4rgQZ)7&LD&c~-&EI;4c+ju z)X7a+KO|S&6#idu!h04Ea>YmX=UU|mm14QrF2SNQ=DH-)N)I*-4*VE<<1Ul_F!SYR zne$=n8FWoDtWdgOJd(N}nB1&5$~4?x#T$EQ>K@T>|M()eLwy_5_^oEN=Bd!juw{)w zG)Y^Vq@_OfuTdCi&VQ-(s`)qJz#*(wAUYd{v;g{$j81%0FGoZtQ0e^cTFM`l23*LR zqCnOrCyS<7Q&z&qICyxj%KTLEm=N3nHNHpSB$WGfTT!ErQ3sKsNZn~%Bp+W44jl&K zO8H|q_1Jf8Te?%4FzXgGHq#6tG^=$pt4|DTkgxYuxeOy6m z)0&Z=Q3}PVm96TZjPCIr)A#1VgE+!Y73uaFMsZ*eM~ZV{W5!I zkBW~iB+>BFb-SL|S0GyuoLY|!4P^eTZc%p|EFg_}aS#h*bp$?=(INLAC^_(TaWZB(=un^)82hjis<|^*=^h}P)yX(%7WpJTs zvW!6|v8-@q{66nDDqXOGpt7f3|t*Wzj3HkllX>+=`ph)SG$l^8oSJmA8AjFjfSpC+$O?|d$w0wVmnFT+- zZ(49Qmo+|yQdgm;b($OEPP&f5&U$)_QiCZ!k=AI%JZt`HP0)uQ*&(zGTd)wDZ#8?4g^xZ&P3G6bFbY4 zb|P?p;{r*Mf=Cez-O3*!fYyfmJwv05dc2UUQx6WpxBUL~Bwac;)YYjm`|BDJelFiZR~kW+rTHcxmACmb`dTluO=rA_)U}^S%XO#!#7Q^|${I zsV2x^sO%y%w$}VuG;~C;FM<0bO7W27!EegMY5usJY%6?r|L5t%E;NQ@h3N9B9^&Wc zzw?o$W%A_x(x{7Ftw9yx(E=n?Cc{%zRysreAv4E&b4Hx_^iY^dAQ^nz7e|>V%H1OR z+*nyTV*N~cime^Z>xvJ_WJ8$mA|6jso>dE&=HhM;!-JsHMcpX^F=;c*mcLBt|O zwWQ)wri!sP`poW6x#8la%B~9oK`$~X0!tP9B3%j(`0jCgm#j#jhE(x1Tl*f7Pl;Tw z#fLqBp_kySm|9*E8o*`i6hj?TZZl~e-a{Fy6318^EAsB;q;q*Y7 zwXUL3Tg;3*Cq|_{nM_^J>efF49Vb!EP$=5QKxy`o7IInlUCT+oY;d6o(cwD-mqgq8 zO7E7%H+s?&GX=O`g6i>t4eRTWZn!nn@^_?7oPc?iYKwr&XX^TGDDCzp89peK4-x7W z!p`4FA=8Sq&x%YmjGVQ44|0bTFR)vvMq&J}SQObSex0n?A%os2;UGyb`PSWyFQCdk zku1c)(5NDve~%=F!9|h-|AW@Y0qXl0LolQ{2bNJS8%Y}<|7`0QibAH!uTNY8AMC7$ z7){0C`xe3Hn|RZIr3>APyI6}7(gqt_2>`;6i%e&y-BB`u$M20MG(m0co{PU*GVP8> ze-oeR-ZC#%-PtX8uub329k(tC*RLRVCM)zb{m4pOph)s^^KwFk*Oei*o5GMC z&F6E>*Ea%BX&QgaHf?|=0sE3BCtU7=a5nMTtziSxfn3}}3~v&|%ZRxh$JM{@Yxh~g zWUQr<`yO{^i#k=~iKe%;JaJH{8Ae-Rzny))hJ7y}>hcPccbqKn9mm%g=rYd1p0BdX zX}gnulySoQFKvUbw+!JIO5An$xvZ)@ros9)-B;r zi8-AsMoyi-yzy*rPmX0WR3N;Q5#p4tVFfL++bFFlP1?N|eCZ z3Z-at>CesmSol9Z+_tU{;HBVvOJES%pJCK6g{WNCRql^cTHT{B3-HYaXA>zK=m zG8JoDX>;)D9|#?3|HVJ_Oj~~cLOJjI55Z5aLQ`{-5b0u@#DSHQ&_Y+`qx~LMVb|-^ z4cp;oipnGj9+`BttTj~YOA{iJ*UyTnKqYAf+q?^th*4X?H3{HM`&I$C{(Xp!e6ATg zZ_gN}Bwim%4|g70%rFeM!jlU;b|z(1*!?41lZFY5!F6p2OB3XD+4;^UY=JQuVb3US zDgit+r$I=6d#S~2^0^7Vly2$E$BQ^` z`Gc9zAF<#E<$P0I$R2;Vzna;=Fvj`%+hzoo``}ACuQkChzB~=dV`TC!oaWpO`-Moe z=jg$%$#>x|p9x~3z?L-(>ccJYC17K+lH!=fA$xseT#vf%^4?+fufO#eS$b-;fJ-nD z(kEhFvaG6w<=_oqJ3qRmM5d_s7E|Ke)jCM)Q%z$ebkJ|TCr0ubA0?%u%y|CnOF}$b z;zPH3k}bb*oIR6?F9b-+dpmC~Rycn+pU4YCj6{_tjRRd4tl+{GwjNtB!Da0%l^yMs zL$QNgOwEkmzuMN{gUx=o*%`Oq|0XtQe&lO~9JV-5bX8XgLGXC~_`cH6HdRTtAocIM z#v-C8Yi~`m<$S(S1?h~uCOV02U1~DN?{Cz)#g?ZW;6a(38Ak1IA-FlNCr>q|L>V4T zC*HB}se-ty42`T9g_9t^X|=VKWyPeN3dvXJxRPUtLS}Dq3Q`N&Si@f)SVsyv)0I3# z2aHEb)>VFV-}nZx5CBCuh$Jplp~~Ah-1W>wOB#L*vfxtB=EX*@2<}c7Dq0-?gs?Fhvmxg(|{0^h_EdzXmT&-xcH zXa4rn@ge;Is^nI{{D~DmkDL+!qY<`M3dF%&R+N8|*$L0dEIvJI^TXAh6s3(B6s1dO zDA&txB?RQpk?0}N9Oa8#0v#dnZk|~F+^z_cQ>Px)Ke(aWNr47pj0r%c=eP5ViyQi| zLTAS^9$qTMPl%Urw6U@A5aNOCl*w$Vl3;+{J`5W0W?M25(4$M{GFfEh9dt1i!jpZN z0B+z!!cc@^6$`!SN@QUfT&Tf1?TRR$viC&TLnE(=@&7coE&-^Nw3`*=cQkqO^Vd8n zG+dD{!!#maCawVTjIDT2%T{%A`X1zzJ6Tqls@T}rkSmjPjzaW+1f|NU9}r9r4l%s% zCe#~*_$?ljJ1?H>c{Hf``1qu1Qmu(snaa#U;5`Y}b#ho(So3t1mBiDvudye7i30g4 z&r-XrRaDR+RDUie28$|qS<;l|WnU6z@Fnxn6AF34)Weh1hxw>x(?55X#g7t%Y<_Az zg*ZkH&2R$1eAWghawE*Mo6a2^sO(=}!y<@3ZN3w6G%HPfr|6R>$VGoWjGO)1p*!Nt zb`Kksg}5$68^E%i%*E*X-bNI>%1QZMvzL1SN)-3&EURK#TU&)8evBB@4KB;=wu8{* z^|ikE8cr^*9}xKK#cS`M$q%sb^Y6j#Sqy}iyr0eW>I^84JewvC2AAl-Rj;C20rPV( zxmG~&oQdV4>wJ7}U<`dsTb|CIfLfu64vn?+o#JL&f0PeyaLeY(eDtFP49$qMr`P_u zvi9b*SlI!-uZd3o_8GHy;SLMFYC~i`r9AqeNM+^IH}fJ1b@K!m#aSqTF(Zn4q+Ad) z10`i;9ET9^VgBPd=&Y`xs%p1C->7rg{`ZGPNqL>${(PZJPQiy|<-^qIFazyG6@#BV zK~b#3$PwV<+kx-H!xUY5fDm;s7q*!x=L{bCw=+=@5fRFPUliXL1>f#Co1EeAM+=z~ zY2r;Gw-%+ZUsTh*^d)wgc3kMA%M&sm7fn4Dd!CM&NRFxZ8N9e-S}nKboAXPk^(fR` zSdf#d(**HyxxPN0FCQBC2*3K;m-;+WXLZ29lbhf4oaV%L%`1Bv}fu89#$mlql(E6H<|MwZ7SxF;}m8k zlKLi(D$@mXP;m${F&3PXv`lci3)cS2afJnSnuCID<&OXwgbg+Hufo`*C5izF7h_izK?nWl>)r7XNs$Hnd`A^%PCVRch)(2ET z?b_xKv#T-_4*W3NfI~5U+s@!H2G%mM5c8lHffziw~3?MSe{cs^E0<+!pP zkx+%^ySud|G4BlbJSlMu|EI05KGvdLQBYfu1wt76ecsw!Ytg2L-(Ui)`kUKS3akJX z&W$akp1d(Kk^GH>K&ERvrNYs-z0VZW6{2-c5KHue>pPu|G~*Vp_O#jM;;+j9F2qLr|B%fN&NQdfg9mfXDH4dWzxe5d^f2z)^Gj&Qot*E*FwK2ku5Dvb-&wd9SOT!yre z(%#-~LH2wK+q7~@|Iwok3IGE8ExY+oR!yp(zDbE1Q0Bn;KfOfCFK^$zjqmO4?GJm& zyTBgbqnj%(KzVl#NYq?Jsp^mKOipR>`d$^~(#Zh3uD?+Q`N%<1EJA36X0ayUqS zLoegrI&_fs-8kp_H4_l9ziY6j7#WEg-AZR|q1La(yYBg(3tPlsF_{geLpTDtMJy|^ z+iA0K)}|B+<9cUCsQ+ev^hhGcK0_}D?lPVu<|d!aj!*miZMw0ek{Y`U&)nw3HX}oO zBUoGOZCRn0@0iLFz}W9&&p(s_%$n-!*TOkD-<2hVdSXzx=RWr`3?W)}rrX-&aA*Af@KwLs9l{?hLhf zYHvAiT~_^$h^ZFC0%I1xC(PFND=F+V$YTL0+Sn{F-hb3;odpGrN@dA?2K(Xyev8>j z#h)|ppOXF;29x)*B*vk_La%nH^%QGa8@{DcEWe})cvFKc7Y)4p(N+%QA$ef&7twDP z@1hh6(-#WadSsM8c+aC&+{eL|(*YLF&qAUcXqAkKs3}cOR!fTxjQHXj3;wijgy?f- zCiVKPZ)TG4Rx%N{^8y;P_bxhzqC@Vx(6D)87yjkZ>v-w8ij#Lv&5*3t@RMbz`cXqF z3(kX;0JF58ER!#nH{fEgHR<+82sZ4Md%DS#CGAvjs+6hN+-GbSU#^5`L|Gdour@*i zmYY_F^tm?D{odVm`rzPW0zP2~Q$?AcjxMGPPmF}zccTHkn3>-#OEl|a%f$Z3v;S7D zHDVaa-{v>+owkijP8?d+!+&?KC~=K(P&f#B9-rS00j z2W(($Y|OiY!D2CBE5E^AjKp?8ud+nlJVdtcqmUCdU7Ova{DF90J)!d z-`;K};C~0ag$5KW$I7mAn~;N6EE))nrE3^DJS*lx-xbv>?DAmY#f57tG4@k9;b$(0 zmqfF};aCDipxyatmiSZSWKA`44j2tz@LhYAUe6L{z%J~8*YG zLgMTZuPMj+5(iH(<&1~W%opfD##8!+4Kj>k>R^iJnGi!ctmHj0pg6^EjN@-iQzO_T zYFj^K-?+>|5N7LfogT=Xzs3ZB{$pKG6Z+{)wMzz_>g;CYPZl#)m(O?>00JUF{Q<%_ z2Gx~Ehlc3WGBYK(czHkO!j-i_PJBva!&IF8Puqx5&;9$BoS&wdzFHeGSDqPD83EVZ zv0=T`%eFGLyX{;blpObRj`Qsz0vUkjy?L?1(OIz%{~htd;{K=UT<`x>gG0&Rx~GGM zXksq1%Xeg;xFG5@QKl8Y7d`p0CK=Cuf)*x!wZGOROFEA)A`im!G3~MgZF3QB7P^^_ z+8lqpd6bq@_8*~tf|hbY06>HOosrRJNgIn=0dAq(~ zh4Y{K>wrZq-d_LQ2k>WBe3M@u4Qz%4Ej_G!N8gdWg%wXNxQs2|ZNJVzi&)ZkKFyH} z62C)`{@88ume-dz8w?1H`=_tT-4o_9c*v_FUk2gaks93s*$XG%H}1Bm_P1N2LBr$3ME7K zuE}0CSN|kj(|M__XYGZe(oO49nq=5JNr+!)RegusINhUIIx_|@^J$Eloz$jt@aeQk&VA_)HGg$4h*+R50~FTp(m?3LEz>kL_TAe)u$%Y9aoi9dL|t=ra4^3pq1vZF8bZuno-jD z(VSOR<1_b!sW_4NIdcOulPBian|Xd6G@8;%&nIOYnGp0yd?tZoeRh9#cJ?P7`Xy79 zB32VCwK&0&J6937@7NzF22C5#Bg zA_x4#?J~f*5r+`3TS4IlM$n&8&u`@j6=V;}Td$L-WS<@Jwk--q+Ukg5BlHymNAgPa zwy%`b)xTY4q|Qx%M8Ce~|qhZI7qT1#!7k9QL+WAC|8 z5fTA0#LS7WU*kOdg@Hz6WNIqDi8s|uBoC{l3%>dNW&gZW9Pp<`Sa}N?l?`cfH2;%m zvf2~ZWJtQp4W8eJ$06hLNTqY=`Odj8{V@Ne;mxc7GKA{cd2jgl>u85de}eY)B?vd! zXMWiE2lR1QH20^|b-NtLAl=5_QY9_RC`tZEIQEX~Y)>Y%`xW2wPh6+xG4B+do<6;? zJAJo%29fN#C6Chuj1=!t)8}24Bkhjh@F&w>`|=P3T_%kGz~hj;!}pxzK=9 zQ;Zsz_ed=&$6P}Mx}ip!z1jGic867L&96m5$YK|Iu}esZ5eCY^mzfzp^Y?=nxYL;N z#r0^fh>o&f%d!%t@RvOs5O2wtL+9!*Zs z883$eZb_I;ML{RD|1PN$`~zM@4`BlyA{k%&mY74$f67+FNZ3gt*y~@D&btD7Dn<_P zikswj_WDxY5K6s1V|g9sB0c&u$*M*Uln58VnT?I@AJtji;uR62vPHJrbD4Vp>gqrih1CpI$Yk926UH9rInPn%I|2n zOu8f+2i@sXsHvV~yB_)6Dl?E)N9a@)$@=wlH`=4xq{Re230UST1r6|FQ*a zb6dqc)_sxGO!Dj`vmB_ChLCVXEoAdjEvYIoXtmEFUp0c9_G0exLxX%F6c3G2bDZ=> zk)H~s-E8pl@VJwW<-%M5rkFUtVgvqj=MC`JY)ec3XS#Zo80AS0QmXBmeAGn$io)ML zu#<6(kM5OfrM@cE&Q&sSnA(Zw#P7OZsm>nsA=P{nYwijcjT(1nOuZIvr{dx5^6ZW&TZ(D@LR|P8PGdxM>K((sE41Zb#?mz2FzNQ3u)t#l>G*oOM^1-_h05BBj}fFPi%kf_D-3OB07yFE=qokt6# zpK;abu(hHvu(rQ-oge*v0jikH#f;j7vaOhn`kMRee$pg?@HS;C!0zcR(o5n1YS^;@+`vlc@RLQZ)?Cx_Me zC|0!;q21B+JU6`aKehLelSwbghS_0ABOqQ7lim5gFY>v}TRikfV1Xjl-2T2A2^D%3 zl)pao{$$xLAO2iHnSgQlnc2|^Y)5^;MC>A;+sk2Ja5xMBFR+={Z{)%%x@*`dvFCmu z+3O6uD`X|>{Eya#kokY~jABVJ#vP6(-_LmwoJ?LU>87@Xuf292GtQfG9odkq&U!K@ zP#$wh3ErMf@tb6VEfG%37hGaOEdKnuWcFt3hZR~P(ro;{yQ$?ZLs6rQ*tfGcz1W0R z0oSjXLuZTEtrFp1eMYBg3v4YfS_U7p8m+P4y_vwi^K{H&Y3SqaWHH{VOiAPY^giF50vs<}+ z9FQo_?~F$nNTSQ0XrI+dP&6_sD5p)LTDMq66WmH=_Vai(;?%dxuAre$0W;zp`y8j< z0L_O48N%4{^`)}1Ar)Zxj@x@F@=lWw2gibjfyZ4GwO%%tALpd9rln=ZfXrbxiA;TxVLADYO7hZSS zE7FZ>?Tm&&UrM-^RvyNm^W1O|v1mpdciHQo5yOZ$w9cO^KC(`~eF-Y;4-CB(Q}Mv< zrFG%q#7_~D%nLcw|0n5sGx}b=;39Wn@B}*?gdabE5N!tF>$h(rE)N1<+r~}?PMBS$h3KpH4dO78p zyUz1nM_dyx*a&6Ql1ZdO#wVuMH;41w>20o<78`a9BXA!i3Q2~;e}vl>3eyJT4&vFf zWJaokJK`w8_hy)jy066T}hG%WS4Vr&2FBbYyD2b^H8~84AS2+VOnZ zNp{6MDULHyNc3~L4q5v4!@|owtC{)v7qPq_tLsv#@xI8yJiah7Ocv^V#T(}L3!or3twUB|iJgk*9Zwh{{9<)*6T z)|~>x#`*#m(l`FxW!fCD7T2Jx2GcKMotUfC3&78{Rm&~-4&S$~N;1~!@Tt}14m`uTNfym-=(8nFi4oWlI z-miRPe=)3T50<0|X`-QoZtlQ2=o|sLcdechM7hv6F?nlgVlwq!yqUtq!O<}hhH6LI z3e&DVv5|j6bgjL8d;gdgSS~oM_^hIDKBi+>pUen%kv@tsK&Srvr!9-!nfeLf9)>;s zbVeTajJLQrqfH^d?DMqZ(%rY=9Oe))f{3$rl;3u|sBP8;l(bB=^}!;MG*wM~=xFEP zl`GGd8fPj)59r9ZGPpn{OQze)SCf6BH)ulg6||KEqx?5BFvLZc}eB zVfUz2(0%n9-K-{{vq2?V-;MI117y#50saC7DNm^Rm`7Ic|7VbU^clk|=x7DpWRW{L z=F#7DdYqKxUInOY3f|^tXG>9=?d?`0h=Y%(2~dgu!`4@ZWz}?TU!-&?jWh_7(v5Tq zD&5j84I;7W?v(CMY3Y#e?gqJ~Lt5Y)-{}fK0Yk53IrRob{ByBf zH0x>Bm*L{C%g7(&U%;64YJE9LQl?}I-$lUtEN1<6B)nN798)jphj03oR@8R_qj0z~ zfYtIv#n&s~A;!YIA4U+!`Wc5_@0W+I4m^7j=3?NpubK*i{fr_`s5t>KlGo|`iGM7*P_{Sc(A3nF zwE*)>vZq>sV2leL(h*Y@I{T@`WOM~ddQ;{JafdN%ZlIE!C>#JjnB$2`0_ky6|Ef=Kgi_c6MW(sbl;#=ODo;cJg0@$b!jKwZ}Fi*$LKt81mma zX%J*_%oaxO&DnI@usqF>qw^G2=`gBd139PuTdFTx*c0n&-5F3H&b8#4@PO$L4-A}2 zw;_9x3XM1PuRt&nl0o7*`F6sw$DL%&XZcje;kZ|mFf%!n98}ANm|k~Xxt8NMC~-~t zYd<9ozpT@)aMkf5UmqgJ*$CWu>B+U%kE@$0Te4adqhjeH2-({=v|{);uB@qMn6aC$W} z#i$j6&<;{dTu=p#?oBg_r&WYAQpcXq7Xt}upS{Caq<#l-!q0B-2tE`@Q(w_Nzq&=& zw9Ki`wU4pef167&{I7xd^3%)&4z`Sy(ZIVce<+Kv{1-bp`omyu zAQz@z#36asO{bS~8{QNglvx}q0^6tp9IOs5u+TQuICK+NJL5tBZx5+{FvxCA#`?uSP|Pbs~A(N%ITl zCN$Jv=s|mfu_6e<$IDIuFG#_}u#C|YYdsj=HgrVmQj2%4Lc0rPQ4^lgoe&+x~x z6f{+n70_BT5ZQ@3=VO#JE#OWV$Whu!a#U0`H1eA7!mtkp@98TlD#~A4n0zacrcoKP z#&0q)`KF9fkoW+01+B}zP0~~Lk_0?Z{u^m=5|&&olreiIBJ1Zzzgr?(&bnWTZv8Fz z*y43HmB7=@KyH zpo}*AOZMLUrH|$pJMqey58$8F8(NXuo#!ouj2^Kc!Nt;6^zHZh%KQXoSf@7hArVdF zWeex%7SHLAQkcm4tjfm0fAF5Nzh%08bWprkyA2tNS#a_2rha;=Yiepz;Nr&iJFLLP z(dLz_TT_YQxc~U^qmQuF&TRX|=L;g{F4K5V@t@LUz+ zNefe%&no*3Zn*^5DQ`Z9QOXuum|nbCH3lbBH(feW4Rr6@E(1XS@U{Y_*n5pKf`AJ1 z6{ zS}N_`t4_H7)Cu0Lk=Dm42UXD_s`FZPy9}jN)0y|5evCr}DQ6SGnbILbMeB zYn?775tqyg@61e%$<=s=={< z;DCUQlA=tGl+j;bb*8{Q_cIgZKYi=XH-pRGAH@~nz47i0eBzxc;4N2X@Ccoz3E3)J zHYXd8bGdgoJQ&AzJqaUbKU#6Ux&C`QD(c!e|Gh*-wg?hrMQ>uRNaKRi5Zp+AqDB5I#I1>8 zRDq|%>RRyy=BK`(;7qx%c2#BJxOP7ix873tvSC|e*TY0`6A=sxk7Jl=->VW0Sk-^F zK4JUs7c@>-UQt#?Y-9Q@GE9JdBy4G`)EDx;ygoX*Wo}o0M%o$bBkOLM1PxOi`O^M8tpI6B84UFJ(s4K*)PSt~H2%(LPUVOyGNR z&9H|O?>|S?`sr`bv}Yr|xZ^iFHy8CCvm#NqaUZ}?_`zAa-}5X`MS#j;lgz;OZuxMt zH(Em}Pp$Tz%cuz>6^0I((3k4Lr)SvYiVSTQ zl~^Q7`-XoA>w=BM;w{u^1ZijxK((U@aCJjDTEp`qY1lK4=HfF|?m`vYh8%dNBcB;e z)iuW5+qUEs7Z+Rqe{X^L`G4foQK;ulE*uI}Glvfi$pdjL8lcm|^-h6V3PUMNB=W!i zECPaq{S(~3JUl#vmZh*~dR=$4Aw2k>*p@kUe130rl5jJyCg%INNh^k;{_q00KsE!XSe)(LQ>IP+|3e-mCG`rx9l^Ykw04fKimcWPzj9AKM|8e;i zZW@|x$MtdHGA8e>H<_n}l~qKrHG@-VdkA&&HcH=_e`+9w_fJH(M3SEYQ;lMuXFl%8 zdpq&lA(!0dW)i>>jwD@Mx|5fGFFT+>E4QZjL6ywd)RCXyU??nn&D-`b6sB{YQ%{Ex zwQc|31eL1#`H(&sNI+gdiqFGi?9AM(Ze_8E2hYiQ%7gj&SS>-H^>2lXT6!T_=l8{VfuLlakynlA{UJV&`b-c=So%dsoG6IO|ymBzLMS`_pW52@#`?1r91nsQYnXnk=gOQPb%KYvzrtTK# zm)dWU3=GakM80|U<$xDIk*EgrQ=@k)93F_G%Ie-!O}w43-EhRk+&5wC_4nCr$Ua=60iq0A>+bmeI}|7=_@z z8`xEu$FD7?$p;2!mN5SydY0e3|2`6ywYFmYvCLg-a{t+Kk0#IFcKDgrT1`U z50L&oYF)LoHJase8nYe?G|s9&9h) zr50Wu3gmO6_|NCo)5K)9kCR?@CQ5S34QvX+rtw1_2sCner4r!3P;?gkMoIQhd4v*>!cUnmS~BZRW`@QVRulwmL&5vRUDxUVnDi8vX{YxB9M8qsti9@}vgH z5~RmpT`zwtenkKD6Nq`Ny9Ptd`6S3N8@SVG_0~oQ*k}AY621>vx>RAbU3HhuCKrPG zd&uiez3>haS+BCti|WqHGAWL(owvC%c$L3TvbY$jsi`qAT9SuI#nFCL zQNbmg=ejoj_z@52z1RaXie}*KBG~g6SM0Kw<<{yZ-Joo-!p!-i1R+avhyqqZ*8ecG zhTPzO;u)B|+?ZAqS3qt%*!+O^NBizcFOIP}9p__frEDE;b;paV8rYN0z z6rRgYCa6XT1v__+ng`u)xVr1xFt5D0X8%cr8p_F`0+bJ6R?i2_Y;C_xt1>n={-CLe z;@>sV!~(Ii;{Y_hy~(2hqpm-FJ{29h$5Jqx@l|-ZfHeb0Wkr-74`*MLB9V63OvC<} zUme0G?U_~Z?-wISKD*r1*+DHTWj{`K>voFGrwjm>lale zW!}OJnovyAg0+hqCNWXZ zt5h9=)MxAZmMvr5zlE@e#I2<;b3de=y;x&@V36i-+Yexd%^!LhB}_j=fqP5=4uHsk z!t?Zpw?>i})oaywe&qDpR9F=Au3axa7oAzMdfh;Q+81sBUUk{#d+2C`Al%!cX>P62 zqrIY$eg<105?7~f`drMH=n7SGvJzmOtw$D;2>ovPCv;#e941t^opqI0698PpQUJdiMZ|;AM#fc>XDx4&BZYOIqKyoGHdo2qh3>^OR5Yl)Z7<5|bZ2N1NnF-Lj zQp?KVf3GwWyqZM(y`Hoe<5g0(@eAv#SDBR+X0K^b>6}Pq(A*#C=^CUV8OS>hvA^bB zZo#403?5=d&a7NdT7V%82UWyxAWdnW;}lm7*FH0^q^G-Y(i^#x`G+f;!xQpNc~>$F z13qhGf}aVS)Kyv@2gdIF-$XV@U9(|`@TchKU_-XL<2f`m1cWsI`zxKq&0>(5m>AgZ zbuR$rqzX2fzT7;5YtG()+HJkC{ymhVgv<9=C48Rv=4>xc?83LLWT`hZ3D>Y5HZzb| z;}b17KIWQ+*e)vyU*(ps2Xv^kdPZ%1eEL!9orCfn&&Z`@VYu3`XY6pfvC8$oi5xLU zaJmP=ptc0+U z`URK#4ODbAYAvBT-v$Bi*{ILNQ3tQ~S7>-Ox|NV9F0t1VPJ)ci3Or4CaxJ8>gR+E&4dCWIy zYYooG_ya8z-}(n6bxJwB$;r`I6?AlTd@lP5<*fk$h`lCMw=ZiZRn}fEv{fCAdpmsC zYv7RyU2qcGTjPxn)$dhzPVMlD&SBNkt0`?Hm8b$4RH=6&7NMX}o9S1a&pZb&>cLR>azoXMcl-hTA`bJz;&UpM$0#VR|5g#-eM zFIfT1t_Yea*_13Ir)_mNomee=gGe>@gFhHk^MN#y29p&@LzI1+esoL>4k9u#kAo*r z5=rNC0Pu;I-M!(2A9Qsi0BCCq=n(%aYec`~XL#OCqoAUS+1ppwqLisFIM^$2MXDM= z?TjDej$h!gENK&y&+zl7p!&1UVQn ztP(fm7|Ayw34w@(>jC=)fAIn~q7V*Yd&*G~Wo3E2{`t0R#6$sEpAP+*k{u4BZF*WR>;Q7!X8|x3vg$8YDHJ5jT$~sqU z5AzzL9go8X!5VKOtm}DRW6bWZKh)d{+RgoUismNNk`1kWp6a|6rqDotw-X!PPYPfntJ z5uR-U6{OycE`L%dL%6?7;a8eugMysOZOTE_(61fB;yJGLuMMp=V4A5^(dCVr#q3=NfeTFnZ`bynOnXDlqn0T7$?bIcw! z5LQb}Wvy^0_v|~TMW@$1gAE3@O9?(~mR|{d_!7&dOOGzN6ok~-(%oP2 ztj(aqn}Ln3zd#{*qSjm)h(hyHMaREVrUvp&xVX3)wh!py;bZMzKlkpc-J-sU6$Wic zV{SoFsKRyVT~9w9og}9H^|zL&jNt@G=aab=dm6gJ>oM9sx8uEH(H{1du4%$ruOsa# zT4DjGUy3hbjnnZV-l!#P09M%ZnKse++^l6?M{+WiM4II$VMElvid2xCqr@G+16UEh zXikuk0782bAG-oE3n7UlNaagkUC_Z?yQQ2G!{S><3^Ns0Ao?*Z}D1xfGqSJ3x) zH)j+Qsw)N9)uhkq6{{84A(j#~E~=mM2e*P!d*$Qw%b$hLestkM9g0&|fo6X0A zgI;~;M|}4Q2$Q<;yvtoi{K}CIJ1zDd-R*oO6fqqi8tB(521C zmO00`1<1k-ZbQ-)kRcw82=a5;vA)B@g|bO!qfbt}Bn!Gq=ZnSpqu_gy;6eBY&GSZJ z`*o2%mm7rM-U#i~c}D*YO7c@X=wxaTB>~{c+v2LVixs~q4)aMl?Pm9>s=OjL-Qto8 zr@`R&>S`WucsqV>`mMI+PNA9aIsMUn_q^3dUe(IvDDfFUGD{Md0Zk$VPk~ z0(hjrr@fDjOI$5#g=M;=Zuf&!;$L_uUJL>it!7{qiei4A@f)Ur8KC70sc+T=C_6W?R;M*W(vde2aMBi)v z4CIc1_)~m$u$oEUDPavVG0+ao8+;>qg#bZ{0rv;!soxqfT=3Dbpb>wGM()Y7q}O=P+K!fCjvR_|E)(lB!8J`c9WTm3_z#0+l3%P>nf%j|*I z%={ofH8B3MJ%n{b;r0y}7^l(B zp#|FlF3TR&%Q;VC6eKTja44bCQqUtEOJ{q1em4C3@}4DVWT|rAK0DU)-{r$Kl~$UW zOxp3kYm%w})co3pm;USmB!5hD7dwBA;eTGD!F%V6hx|8~ZnOiDT|=2Z6{7P`b_Y+Z z@X%kF|804ajvCQv>LoJIH4V(}U{KPZcgNRY;D+bsj#ZxuE9sM0yZp@Hib8Uq!Th?R zNKscCo}I{-yKZ6!)5`&|k_Wxfr^;?+^)kZSo^V`c;DUhZgFIdzm$%t zd>(YvS;e(n-gZd>BZp_lh5p7N$Q+Cj5iT{@b>nnf3J1}q&zaX@38z?aRsXmMfYYBr zCa=cG|Bz&tM%WC#J{9Si!-t*iuMt`p-sRhSy<74c^15SPHZ=TKvDQGyO8jr zaYt@cwJzxV;u75wg(X8{5QHuB63`ELxn5&xddSovHVdn32kox}50M&}h* zFo{Hvh}m9qvG_FZ=whZy3*Wprb+p!KM?S5a=v&L)t?QU5Y@>niEP^%M6}P(j4w1x9 zQ${P@#r&7@Tv}KJGVXSXUgRX@mr!jIc|4Od&TPI+t?Rm*$Yy$c=|_3|!+jufGUHE8 z0ct5YJsowDA-OpQx#LkYO};+dG=nX}{yrZYHN{$9nr3%|TR^TA&3^S90wc${`3b23 zkALI9sfD_TrT&{`Q=FPcNg=1{xI5Esc~*oOr>TwnkX14CeiC_rjkRqh8!lZYSGWlYm0Vv1ykoWFO^_J>LAH2pzomE=}4kK~#XNBdTPU(wK zHx3B+Q!y58nTibmqGb#EoX{<=&Bup|qSdYy*uQV-P9X!_gSRE=4Vk-Du+t!PJ^k7E z&@vElP0-4C5_5q4QnZPpT(0|XnjbMbs8sw%Y=n->SRar@n9hhO66aZC5_J-m3HmYT zgPp?;$R<~D!O1yi`t~TdoVmx5K;wR(qK6WtyYd%hQEW*5wxkX*#;00#R!KkQfw1lm4LA3MM#V1F8%vLWE%5&cf?`{$9dE6J#;n7(20q0r

D!@e{*-7t@vS*f{aVXCz09nfs%rDxs)2}i_ba7;l*y)R#R&Vr-2eOi978vmWWe_HB52&T9U1#9%|X_~ zakSe_keqODG8EaG8|JE_6}$aOsvnVRmOXPOJ~Y?{c~JZrf<=<=s2BX`1CDYO7{<$L zLHV6zy5c91lW`m_Osvjf4wJKH|CIlR{W`WxiECmG%v*|;Ff!`V0;q#VL|#uqFElp= zmX1Vu=z3pcSdHD?b6oitaMsAM-e$i#7sB0=eNNTMU6+hYCD?Ij6aDk1tKC@%qsRWf z#xc4f?L%VV@bz*g@n9_drV_;<;_v6MC;0ciOo<;})_WmznttRU^4{))HLIH0|Lu#k zZmnw*!uZN@Mgi#ct5jODl?t`qkSn<1F2k1R_ILjnAD^knRIu!TKy*FfAQN7I zX_R>|^B*t2%X>>epWDqrLJ#cQh#b)6isxop`Kl)9 zaR#3^6y}4c4ZA~^G+KV*-z$)Pf1h;!Ng&e+`3RM4 zuU@hFYM@gNrGka0>$Y21%GUC9Gtr>y!FeMYi8w!c;LR7UF*PLN6u!9(9^;Hxw!I4p z3E$C_x8+X5gLPe<<0#VxwW4Ur+xcOw_om3+k#y zrr=M+gH?%Z$ytF16HfWhvQHZiMl3t?MJg&2!&NGZsC7k?5!=sSIHvfAN=Z!YU>73n zaG=C8fs=!OAvUjF&i3=^*^ttAnxaCvj@Ut{%RfgvDh5luFruoveY61yFJm=nuU_ue zF!5WwNn0;le}9CB-6=>i@R?fySL&*Ic%UImM16HR2UCGUMDBUdpyfpRS$HS|d+Gm$aie!Sh>n5TV z(hm9+Q(;5l?tyfXmwi1Ff5Fu@J)DJc?jOcC9}rYv<>%Z}#VnPtFMX%KXo{s}ZulEK zVrA>fEn{gpVEbsiK0X2iU&c{E3=8Yp<8=JvcZ+Hp79g?(cFzsM+!0{nra)n76T zA#d@t+?V{^FH5)d(TAP%dVDTx*f7k3tnzFSqsNpQUU#MOUjj zx7-h6mP?{9oJSHC?IL5JA)Wdy{euAq{CBT^!Nn-7=QM4tMWv^L-Sm5=V|NBS6GwJ1 z%1l<)pNrY90>icR^_UchNHE{g^-5i0pv{~;2N$kN>1;Q{DXpCDQj7A@UU%XSX9mIJ zkGc7_YVRsG$7%LR(CJ*FW`YBdo^hAm5#o}Q$yiwoX^^WYNip6)u<$v0SOF2@hTCb1 zrC`gFdPUP-nuYosY@addTKkfJPeYQSTI-|jFm8RG5Yv|_@rv!XTZ&6{ei8E)1MM`N z{2N&ovla0&R=U8JB2Bes1#5-Xnme;#*~fwNOh(r7z(dPG>1a z3C66q|J`q6iy%NpJg3B#t5(Y&Q2b+Vn=ZDVH)QeL2|G$H>xBu)=V*TyxHeY2!rM~(w3dSk+f6Fx9_W2?V zI2+a`=|A4rEyp%B4V=nqL>s7T|9;QJ$g4_d$8X>4U<$cOYTCd5sJjFE(ST{zS41F{ zN;GF3uxaBJh|W0K54(BpiM;eld8w2n$gZ0iW5Zy{0xb(YtDknc%Q5q~?DxDF1YgV)z_Lti+7MGqs6G_Tj#dftNttqtrli@)cI zJqs5{?a2nU&_(_muw1@UB1-peaH5Za)1L8|tMfI^`N~jD7O1Cvj7s<#32ExOv4a>nq>Y7)1 zDVk_n8Hpz-XiMAsjC~denaVFF>uqyVke0L=w$0zHa$)fb z#_+GVS-ZPIaQ5^)qvtXY*_DEnXv4123}i=7^BUuNT?vms$Z=ayQ#noFn7lIUl-soob+iBItg%IE0ZOwWs9zuI$d{Os?(~qOR zkKTrkj1ts6J_=~p`lZ+xNAdaX6GyKyw>F>eP0se;&C|a zWS`+0-dQMnE0pQa3(zmGwtqb8RQT|*EC6*}VT1D}A9YMG%p3j`+uRmkV+Ha*NIVyi z>}})uH$E}gKagN3n{`BwXHa+)jk1YNDYvIFwft%8&#EgejD4t4UMYgu6|F+Js zazA8ySSQwi#?Y{%J-j>Ym?DMju!PK@lZ)>rzL-bWm*`o&^(?-;#YR@Mq z_4aVRRFGZJFtKR4xlG?^eH+)XyS*)@d(xnjJK= ze4#WxD_2?B_f5_o?`-u(uRBhjPJXoJ8o^L7&#pU6HBZt6+@{=$EK^VC>?|$qF@VVw zuTC$28TJ*v7OtoH?cV|A-7+PlI?BRz0#s#Xj3V*vF{)WOp zdw%sp8B9G|5vvbmqU5_sMJ$4Y*GpyJuJhl@s_TY2f0bOgieTR`lu?QtesJ5F5t@#o zF5nZ})rYlL296InNz<-(Jc!RKP(V;@;Q$Y(LJA8sbolr$o&S{lb40#=yY(AAF!?pW zK%qu$d*9fe6y}NrDa_~7MWdr(SZsA-fdf#pi15zX!L&zCLuLD%jiKV^U5rwVm9Eri z0mb9Oy@W$b4jK$4mDY}Y#oR*NS(42|>DVlwOVevBg_~!_UFgiEo-!_+Rs~x`)m8XL z7V1n}wPb2zRL*smUlu=Qe7!@dK2+NqPZRIoM%ACZQ9e?dz)Uh+5v7Qwz5=SzsWnew ziy8h!fN!SMg$qG68scX46Ijy+;NxFz_UI%OPW?q!a~dW@r`?aw8Fnk1hV_}gc1z1W zTZuGot>xgqp9=%UZ!-+?=Bh6yQlRo{^}p>Fdz+joYTWh6T7oj4-o}staonB^J~zX? zB5NKtTyU{jNwH977yWa=P#%0SVy2LmmQR#aakp_&JKp%~jqrpl7F?55^>ISel6t9O zlMZ85==N6=$7+q88wSfx-@a2cA2a+vppbSjW6-+ZgNbW6GS_)?qQwPaX5cgZ5);RW zr~SEH-ofSU3VzHIOkf`9B8B@QSEH>B3LS8}%ZG~p1hQlqO&4b^{T~q*>0M^D2o+zw zkis?W@}10kRuF}WjkVCi%XFgp4zoi$y`FvUT}w2@O5NVi97C@-m94xUyj*JWEgEsZ zmI>#J;*#dOpu1d-9;jsWUim0NePSEMkBGC$d_BhtuYbReBzYu{9)0>3JtKe=qEw_~ z{X~yC8%MgB7UIK}v9nQFF5V^2i=1bzL$(~4`;|n4PC6BH^h$!j=UuOmFQv^S>=V`t z)fLhdGMhO5q99N^ukP;gJR7z$%Ot6`b_P#I(M9xK5^@KqAX|=3tqKB-rD?8r0YQjT zm{o%sz~e7D~q+&L1C_4<5*n*RJAv_^iy-s-;c+B;9Z8(I=j zkpKtTT4LL`*<*HpL+TDbw)A2F2OJU&NWp(_rjP^~VNM%3Y*bgZas9)oG(DA7wZq>8 zmb%t1iTs7wI}QMzatmB1%+0Qt%anDn-($L!&rqhCDAM<^>5q2$gIj&qpJvFw$rmz{-UVJnacFNijqH;$QO zXRz?4JN(%-^ocQ|;z%AN;=~8J_^QLf^^wC^0!yj}6j) zUDB$ls;>4?OHV^bPh)-JQ;lZ1dXg~`JQbr~He(pZ%&p~i-k%IGsDK@89GejDMw#Ox!9U$i=(p3;UyZQuJSkrHHI zOdtHMgCk)pqBEPOk0g zI&ao%uitqm%Kr3U8-F=y&3WuvA*sYk58qSxRTbWAXnVQ`vt=gFBp@O>!Pp_@R%vzP z_kM<(+zUEg>)MIzs>K!(su~H07|~(J75b>x+0#}_v{NHCrsK@sPD*L&A8h0?VsIp? z`+rhCteN;_i#Wbkt$QireL2|O0o^<}R0JtnR~X09hD13&{H;?hdmfayy|8~?Q&Hg} z#BX$C)}^WyDfT%=Lmn@TOa2l{{P)nYS@HEHI#wq$bK1!{dCO+%Yt#WbIXbJBW7Tv1 zS#4JV^9sk!;uzTNZBs|vP4al}=t=hvO2ws?w_2k0E6j+7l`iF*uprS$s4NbtpXcun z<8=bIIK)>$%uecy?q zZ26M2?gOXr%H^ScH4n)g$utY}Vv-;jZs_%{e){|13aXOVM1(E_376MuNu`v&n#odpXbOiaw0 zI!zT7|J+<^RW-FP3$AXX?{D)a0WRmCE#{jyZ&0vYZRcYfu0Q-yy zc(@g=8M5W&=OY4Rm(%NOnd{@#P3cjHG&y_vcSzYC(n}%Di)*cmB1;sG4_fhGE z|BzVVY8NXSV2qS91Olm3sEq*HN6Oi`PI+yoyIVY6z@>DuPr&)V_W=O`d`G|@G-uW- zE#_b>L(s5ZdzCt3f8UCX-w_eU{;3ltwU>>D9PN?yg&QiCIa$Mgk4RjpADQGoXmq=M zWL{Wmshy51ZE48}ycLk6wt;9_B)RZcE@I{#bxgeaB`E}=srY_xIf=}hF! zTYy2+XIg4=U|dTh9bw`67x)HeH#88?$_da#0_VcbsRD(X`~QJ`*CLoN&d&kK7%40l z!XYPLi)aD>IS25Ts_N=~hC~3+LMf{Td9 zhR9hULn87NaOR6kOT{fL82tSFaEORP!0YCfmZAZ$>BMhmfED>@6K6z(4C3(Z=g+J# z$~SLtfs`lGyXn`43_rafNDwO5(LKjVrR|<6+w>wLzPNJB%xqBmzqxF0CkNIufwNXp zy1HavFZT;HefUB?z6b=^FDgpPYzN9e4T_Ba@RjE#(_v{q5q@%V5~wTn_Vxm9h*G^z zhM-%w#m>QiAax{w%L9o1#4-OfBqT%NKOXr6^rs}0l$2_r0aV~F2PE!+xwCG&C-Fbq zTdp(N*q9ifnVA{-Wu;7^MadR$Mg?(-f{7WZ4@_@mQDozsWeE_TVL9{FvN1At0daPa zU4S_sFsCHFMnXo;1<)%LbSK-^*GxqLL&GM998c$CE0*`#7vG$cUKYU+SyN-xSHt=-;NCY{NT=$!tjfXooPJ?|I5jdd zI>b7J{+>O1yxER6TlToz4+OsxD7U8bJN3%sOBM<*#4`qFWq~&Kzlj1qQNA-Xs=!0K zb@6b%A({_G{+&{P)JvdPb3tYgC9?@uCQ{RJ`*%?)5WwvEMZsS=zqmL8U_;=%%**+m zj+T~-xSWLmU0y*UHX|bvy!NN%2HS-WNEA$V@WtMEiy{G-;7;I?@^+f*?$2+3>#8;a ziC`?-nW6ptef!f5F;xvFk9Phj2{AFae+XZIMC1MEDqW$j3xP2QEA}6hB0I>9spmSY zU0VlqTInJZhgGVA_C9rL9~_W++aT0Tz-c078!$cXc*E3D``t3wOUueW=<8!a5Co*& zrz<%BTdaNc_u&D2FAA)+x&TETK!>2=xf=SK18l+Q1AqqLykr`{K*whgyB*9`gkH>& zTSDIy>4W!32?^&-s@=F?Vflv9%#tA@FL9};sAQFu`vDlj^x+Nja4rbye=i9%%Jrzx zCv9fRG}yq9l7Wm1Qbj>Q!Mi#PnCqxx16U5-Kb-bbaROPuWPuSX`WB!HQ%GWd(|!w` zF!So9sO-<$Q-FS)hK4G14Q1^WW?6t$g|L9Kam;h~+YMoXAwAeS;DheSJX~l5kW2cZ zk(QBLENvhGypRtEM+OJGfC6kRa0$-uSaH=!ag4!g8Z#@tx(W^q%mLWQntZCr{W#8W zxk2&$Y~XaJ|D#Mz?VM@@+oB~G9XV$70fXVP}lZWc0K%#w4 z&B)67ITu0#io*2w=;-Jlw6qvKj=^|>a-hsPH845~2*yCYvBnG4?=4W#`2XWofg$Trc!v@5U7qqmr-pT$@&JqcUjt&NY?wxnds`GOg*O)UYC~GgP8aRL@YZw6h%1ChtkT?($d0w$~%fGD=Qs*^O*)f=F-#C+kOIIPLRB# zIy>J26Y3rtp1ksM3|cwr|2cM#atUalyv2xA-xOH8zmKTBX8p1ke(Vq$ZxMEwtB>2F z9T@msljrOa3iHspTmU(T0*msw&yluo^e<*X0Cd%Y6~PX5xM+@ANzW4rW@PJcAq|+| z97> zr7fQ``c5e-x?XA7w@BR%Xi9*<1d0kUdZVC)L68@J#~|iFB^b}EGdd<#fXoj*GcTof4 zB$!gu&b5opU7zejfij_3G(;|Jf4K1G+o&1Hh9;(_RDjY2d^28uxTy|B>!eU_JzGqv zju}{H3=3&mstQfq)z*9F1u`%&bUr@ZlWM>T zQpW-zVWas<&hqkd`pNhH$e31U z8pDy^ga#ZCRwm z7p_6O%o+zS)&GyNw+yT5d%}hf-O?o~h#-w1CEZGQcXxMe8l()QK_sL@y5Z0w2qMzm zAl;46;`jgZUhnhe0WO6T`|Q2ftciPO?$Mc+*Zi77+Ho@UN6}G=WDJ4T>jP_g+z6}y zVT#Xs`skAuzvW}N*>dYT#nzeMLrZ*P;}qLw5&CFn+2js_n3k1+0Yo|cSvdBgIjpD% zT`=pBOkNdN{%@~e4N1?_o50=%n=%+Uj==lL1!9Meiz^F;;F)VE1K#n6&DY#4m?`92 z{d%e(FPbrN;7^bBhvZ~b;Bo?|A|)ltyg%Xj8~Wd%Mi4mb0yzCiNYbi5GY`ea0(61W zQnZklgscILt;^N%x{O(INeSX$wIazG!Yy4J@K}IAfFDlmu$dV>V5NX?EQ3L?;$VKq zgBt^H7xKs7+iJ&abyn9| z^|e|qXWrn}9Tk@$9Iy@?swnZnAbqgkKQCaW_o0IOw5U2dz4Q80%($H+Pp6cuZ1?9O zzz0h!EKCKQDH%XB4le%TjsUYpFD%@KVLJ7L=m`U^E9V?&$^pI(Afe_nfbPy3!ARnV zu`i&N-2}+WPqW*-E7W|89MF4b0uPpP*Lm{>6GS8%F0N2E5%AnVp_98W-cxi$;8G_s zyo{F_^VvWDF2N)JRs=2h-6esdeSLUlcJ@2qXFoV;Al!&|1M@;hefGh5IVn`gjn(GD z-h%s-l1=Y@UE~sXuo#r{x7>Q)P+e8+R#Vov!PK(JE^pv=kV2j`XbZ_A1nh~w2y+-F zP%&u_15D?4S79p=Jgduh@SQSgxwW6yij!~x_;Q#j{?r{^JPn7Gqj&3e8& z9R`9aX$&+RR3s35j6ePH1_KcZX|zwF{%a_sKN8d{iq&;Hf8WWyI2N8*TpeH4m zHoZw4s6J`i+t5Ji$%_wDA+kNMM1ip4c&kq2W4JmxE9cIFRmdNa#Rx^gbh_ zG?fP%6$vzD{dF-!dHrz*l z@9E?5>1Fs|j{TXOTm`nesIoHUrSaLp;^NnIN|-SxGb3X^yD7|ONdN2Dw#&R{g{(fH zHv+N&I;jc3*i0>=rgg=l@X=~o^iaS8_^-gOHstR4$F2Pxy{M%161KASZ#lVJmnoGZ z`V$Kk7ny+X0i%Gym39}n{4&|2s^f2+~X9;KG-$><|?6fK%d~@{DY+X~oFUkP6JLO;0Uclkhw9 z6ZX5<-{TZ4AvRV4fj%w3KY%w$jhp@~ap1WrdAtl50?}0Gm0M7N3Oa)&-cbvuqfj

}d?a;?U%0B00D3ELZu-$qBAa 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 ai>*O!%Yn>Ly%&dI;Po@fop|4glJGAomMcvF diff --git a/Telegram/Resources/art/sprite_200x.png b/Telegram/Resources/art/sprite_200x.png index dcad41196350c6f3081b4a44a364adb8e1c66287..95f40861291b4f7c52535391bab466b424ad8c2f 100644 GIT binary patch delta 37241 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 hMx!}Yjakb_r9g-1w)n@5BX7D;?l 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 diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 570cc6603..55c6121ca 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1866,6 +1866,8 @@ emojiSymbolsActive: sprite(287px, 286px, 21px, 22px); stickersSettings: sprite(140px, 124px, 21px, 22px); savedGifsOver: sprite(329px, 286px, 21px, 22px); savedGifsActive: sprite(350px, 286px, 21px, 22px); +featuredStickersOver: sprite(329px, 264px, 21px, 22px); +featuredStickersActive: sprite(350px, 264px, 21px, 22px); stickersSettingsUnreadSize: 17px; stickersSettingsUnreadPosition: point(4px, 5px); diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ee4bbf2b9..773677775 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -684,6 +684,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_switch_stickers" = "Stickers"; "lng_switch_stickers_gifs" = "GIFs & Stickers"; "lng_switch_emoji" = "Emoji"; +"lng_stickers_featured_add" = "Add"; "lng_saved_gifs" = "Saved GIFs"; "lng_inline_bot_results" = "Results from {inline_bot}"; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index fea12175a..b7d7095c3 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -959,17 +959,17 @@ void AppClass::killDownloadSessions() { void AppClass::photoUpdated(const FullMsgId &msgId, bool silent, const MTPInputFile &file) { if (!App::self()) return; - QMap::iterator i = photoUpdates.find(msgId); + auto i = photoUpdates.find(msgId); if (i != photoUpdates.end()) { - PeerId id = i.value(); + auto id = i.value(); if (MTP::authedId() && peerToUser(id) == MTP::authedId()) { MTP::send(MTPphotos_UploadProfilePhoto(file, MTP_string(""), MTP_inputGeoPointEmpty(), MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100))), rpcDone(&AppClass::selfPhotoDone), rpcFail(&AppClass::peerPhotoFail, id)); } else if (peerIsChat(id)) { - History *hist = App::history(id); - hist->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(hist->peer->asChat()->inputChat, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, hist->sendRequestId); + auto history = App::history(id); + history->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(history->peer->asChat()->inputChat, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, history->sendRequestId); } else if (peerIsChannel(id)) { - History *hist = App::history(id); - hist->sendRequestId = MTP::send(MTPchannels_EditPhoto(hist->peer->asChannel()->inputChannel, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, hist->sendRequestId); + auto history = App::history(id); + history->sendRequestId = MTP::send(MTPchannels_EditPhoto(history->peer->asChannel()->inputChannel, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, history->sendRequestId); } } } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 848bd9955..f3d50db54 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stickersetbox.h" #include "mainwidget.h" #include "mainwindow.h" +#include "stickers/stickers.h" #include "boxes/confirmbox.h" #include "apiwrap.h" #include "localstorage.h" @@ -37,54 +38,6 @@ constexpr int kArchivedLimitPerPage = 30; } // namespace -namespace Stickers { - -void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { - auto &v = d.vsets.c_vector().v; - auto &order = Global::RefStickerSetsOrder(); - Stickers::Order archived; - archived.reserve(v.size()); - QMap setsToRequest; - for_const (auto &stickerSet, v) { - const MTPDstickerSet *setData = nullptr; - switch (stickerSet.type()) { - case mtpc_stickerSetCovered: { - auto &d = stickerSet.c_stickerSetCovered(); - if (d.vset.type() == mtpc_stickerSet) { - setData = &d.vset.c_stickerSet(); - } - } break; - case mtpc_stickerSetMultiCovered: { - auto &d = stickerSet.c_stickerSetMultiCovered(); - if (d.vset.type() == mtpc_stickerSet) { - setData = &d.vset.c_stickerSet(); - } - } break; - } - if (setData) { - auto set = Stickers::feedSet(*setData); - if (set->stickers.isEmpty()) { - setsToRequest.insert(set->id, set->access); - } - auto index = order.indexOf(set->id); - if (index >= 0) { - order.removeAt(index); - } - archived.push_back(set->id); - } - } - if (!setsToRequest.isEmpty()) { - for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) { - App::api()->scheduleStickerSetRequest(i.key(), i.value()); - } - App::api()->requestStickerSets(); - } - Local::writeArchivedStickers(); - Ui::showLayer(new StickersBox(archived), KeepOtherLayers); -} - -} // namespace Stickers - StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : TWidget() , _input(set) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); @@ -215,12 +168,13 @@ void StickerSetInner::installDone(const MTPmessages_StickerSetInstallResult &res if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); - } else if (wasArchived) { - Local::writeArchivedStickers(); + } else { + if (wasArchived) { + Local::writeArchivedStickers(); + } + Local::writeInstalledStickers(); + emit App::main()->stickersUpdated(); } - - Local::writeInstalledStickers(); - emit App::main()->stickersUpdated(); emit installed(_setId); } @@ -864,59 +818,13 @@ void StickersInner::installSet(uint64 setId) { MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&StickersInner::installDone), rpcFail(&StickersInner::installFail, setId)); - auto flags = it->flags; - it->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread); - it->flags |= MTPDstickerSet::Flag::f_installed; - auto changedFlags = flags ^ it->flags; - - auto &order = Global::RefStickerSetsOrder(); - int insertAtIndex = 0, currentIndex = order.indexOf(setId); - if (currentIndex != insertAtIndex) { - if (currentIndex > 0) { - order.removeAt(currentIndex); - } - order.insert(insertAtIndex, setId); - } - - auto custom = sets.find(Stickers::CustomSetId); - if (custom != sets.cend()) { - for_const (auto sticker, it->stickers) { - int removeIndex = custom->stickers.indexOf(sticker); - if (removeIndex >= 0) custom->stickers.removeAt(removeIndex); - } - if (custom->stickers.isEmpty()) { - sets.erase(custom); - } - } - Local::writeInstalledStickers(); - if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) Local::writeFeaturedStickers(); - if (changedFlags & MTPDstickerSet::Flag::f_archived) { - auto index = Global::RefArchivedStickerSetsOrder().indexOf(setId); - if (index >= 0) { - Global::RefArchivedStickerSetsOrder().removeAt(index); - Local::writeArchivedStickers(); - } - } - emit App::main()->stickersUpdated(); + Stickers::installLocally(setId); } void StickersInner::installDone(const MTPmessages_StickerSetInstallResult &result) { if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); - Local::writeInstalledStickers(); - Local::writeArchivedStickers(); - emit App::main()->stickersUpdated(); } - - // TEST DATA ONLY - //MTPVector v = MTP_vector(0); - //for (auto &set : Global::RefStickerSets()) { - // if (rand() < RAND_MAX / 2) { - // set.flags |= MTPDstickerSet::Flag::f_archived; - // v._vector().v.push_back(MTP_stickerSet(MTP_flags(set.flags), MTP_long(set.id), MTP_long(set.access), MTP_string(set.title), MTP_string(set.shortName), MTP_int(set.count), MTP_int(set.hash))); - // } - //} - //Stickers::applyArchivedResult(MTP_messages_stickerSetInstallResultArchive(v).c_messages_stickerSetInstallResultArchive()); } bool StickersInner::installFail(uint64 setId, const RPCError &error) { @@ -929,19 +837,7 @@ bool StickersInner::installFail(uint64 setId, const RPCError &error) { return true; } - it->flags &= ~MTPDstickerSet::Flag::f_installed; - - auto &order = Global::RefStickerSetsOrder(); - int currentIndex = order.indexOf(setId); - if (currentIndex >= 0) { - order.removeAt(currentIndex); - } - - Local::writeInstalledStickers(); - emit App::main()->stickersUpdated(); - - Ui::showLayer(new InformBox(lang(lng_stickers_not_found)), KeepOtherLayers); - + Stickers::undoInstallLocally(setId); return true; } diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 9ba408f63..1045d9bb0 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "dropdown.h" +#include "styles/style_stickers.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" #include "inline_bots/inline_bot_result.h" @@ -34,11 +35,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" Dropdown::Dropdown(QWidget *parent, const style::dropdown &st) : TWidget(parent) -, _ignore(false) -, _selected(-1) , _st(st) , _width(_st.width) -, _hiding(false) , a_opacity(0) , _a_appearance(animation(this, &Dropdown::step_appearance)) , _shadow(_st.shadow) { @@ -456,3446 +454,3 @@ void DragArea::step_appearance(float64 ms, bool timer) { } if (timer) update(); } - -namespace internal { - -EmojiColorPicker::EmojiColorPicker() : TWidget() -, _ignoreShow(false) -, _a_selected(animation(this, &EmojiColorPicker::step_selected)) -, _selected(-1) -, _pressedSel(-1) -, _hiding(false) -, a_opacity(0) -, _a_appearance(animation(this, &EmojiColorPicker::step_appearance)) -, _shadow(st::dropdownDef.shadow) { - memset(_variants, 0, sizeof(_variants)); - memset(_hovers, 0, sizeof(_hovers)); - - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - - int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::dropdownDef.shadow.pxWidth() * 2; - int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::dropdownDef.shadow.pxHeight() * 2; - resize(w, h); - - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); -} - -void EmojiColorPicker::showEmoji(uint32 code) { - EmojiPtr e = emojiGet(code); - if (!e || e == TwoSymbolEmoji || !e->color) { - return; - } - _ignoreShow = false; - - _variants[0] = e; - _variants[1] = emojiGet(e, 0xD83CDFFB); - _variants[2] = emojiGet(e, 0xD83CDFFC); - _variants[3] = emojiGet(e, 0xD83CDFFD); - _variants[4] = emojiGet(e, 0xD83CDFFE); - _variants[5] = emojiGet(e, 0xD83CDFFF); - - if (!_cache.isNull()) _cache = QPixmap(); - showStart(); -} - -void EmojiColorPicker::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_cache.isNull()) { - p.setOpacity(a_opacity.current()); - } - if (e->rect() != rect()) { - p.setClipRect(e->rect()); - } - - int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); - QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h); - _shadow.paint(p, r, st::dropdownDef.shadowShift); - - if (_cache.isNull()) { - p.fillRect(e->rect().intersected(r), st::white->b); - - int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width(); - if (rtl()) x = width() - x - st::emojiColorsSep; - p.fillRect(x, h + st::emojiColorsPadding, st::emojiColorsSep, r.height() - st::emojiColorsPadding * 2, st::emojiColorsSepColor->b); - - if (!_variants[0]) return; - for (int i = 0; i < EmojiColorsCount + 1; ++i) { - drawVariant(p, i); - } - } else { - p.drawPixmap(r.left(), r.top(), _cache); - } - -} - -void EmojiColorPicker::enterEvent(QEvent *e) { - _hideTimer.stop(); - if (_hiding) showStart(); - TWidget::enterEvent(e); -} - -void EmojiColorPicker::leaveEvent(QEvent *e) { - TWidget::leaveEvent(e); -} - -void EmojiColorPicker::mousePressEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); - _pressedSel = _selected; -} - -void EmojiColorPicker::mouseReleaseEvent(QMouseEvent *e) { - _lastMousePos = e ? e->globalPos() : QCursor::pos(); - int32 pressed = _pressedSel; - _pressedSel = -1; - - updateSelected(); - if (_selected >= 0 && (pressed < 0 || _selected == pressed)) { - emit emojiSelected(_variants[_selected]); - } - _ignoreShow = true; - hideStart(); -} - -void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) { - _lastMousePos = e ? e->globalPos() : QCursor::pos(); - updateSelected(); -} - -void EmojiColorPicker::step_appearance(float64 ms, bool timer) { - if (_cache.isNull()) { - _a_appearance.stop(); - return; - } - float64 dt = ms / st::dropdownDef.duration; - if (dt >= 1) { - a_opacity.finish(); - _cache = QPixmap(); - if (_hiding) { - hide(); - emit hidden(); - } else { - _lastMousePos = QCursor::pos(); - updateSelected(); - } - _a_appearance.stop(); - } else { - a_opacity.update(dt, anim::linear); - } - if (timer) update(); -} - -void EmojiColorPicker::step_selected(uint64 ms, bool timer) { - QRegion toUpdate; - for (EmojiAnimations::iterator i = _emojiAnimations.begin(); i != _emojiAnimations.end();) { - int index = qAbs(i.key()) - 1; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (dt >= 1) { - _hovers[index] = (i.key() > 0) ? 1 : 0; - i = _emojiAnimations.erase(i); - } else { - _hovers[index] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - toUpdate += QRect(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height()); - } - if (timer) rtlupdate(toUpdate.boundingRect()); - if (_emojiAnimations.isEmpty()) _a_selected.stop(); -} - -void EmojiColorPicker::hideStart(bool fast) { - if (fast) { - clearSelection(true); - if (_a_appearance.animating()) _a_appearance.stop(); - if (_a_selected.animating()) _a_selected.stop(); - a_opacity = anim::fvalue(0); - _cache = QPixmap(); - hide(); - emit hidden(); - } else { - if (_cache.isNull()) { - int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); - _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); - clearSelection(true); - } - _hiding = true; - a_opacity.start(0); - _a_appearance.start(); - } -} - -void EmojiColorPicker::showStart() { - if (_ignoreShow) return; - - _hiding = false; - if (!isHidden() && a_opacity.current() == 1) { - if (_a_appearance.animating()) { - _a_appearance.stop(); - _cache = QPixmap(); - } - return; - } - if (_cache.isNull()) { - int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); - _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); - clearSelection(true); - } - show(); - a_opacity.start(1); - _a_appearance.start(); -} - -void EmojiColorPicker::clearSelection(bool fast) { - _pressedSel = -1; - _lastMousePos = mapToGlobal(QPoint(-10, -10)); - if (fast) { - _selected = -1; - memset(_hovers, 0, sizeof(_hovers)); - _emojiAnimations.clear(); - } else { - updateSelected(); - } -} - -void EmojiColorPicker::updateSelected() { - int32 selIndex = -1; - QPoint p(mapFromGlobal(_lastMousePos)); - int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::dropdownDef.shadow.pxHeight() - st::emojiColorsPadding; - if (y >= 0 && y < st::emojiPanSize.height()) { - int32 x = sx - st::dropdownDef.shadow.pxWidth() - st::emojiColorsPadding; - if (x >= 0 && x < st::emojiPanSize.width()) { - selIndex = 0; - } else { - x -= st::emojiPanSize.width() + 2 * st::emojiColorsPadding + st::emojiColorsSep; - if (x >= 0 && x < st::emojiPanSize.width() * EmojiColorsCount) { - selIndex = (x / st::emojiPanSize.width()) + 1; - } - } - } - - bool startanim = false; - if (selIndex != _selected) { - if (_selected >= 0) { - _emojiAnimations.remove(_selected + 1); - if (_emojiAnimations.find(-_selected - 1) == _emojiAnimations.end()) { - if (_emojiAnimations.isEmpty()) startanim = true; - _emojiAnimations.insert(-_selected - 1, getms()); - } - } - _selected = selIndex; - if (_selected >= 0) { - _emojiAnimations.remove(-_selected - 1); - if (_emojiAnimations.find(_selected + 1) == _emojiAnimations.end()) { - if (_emojiAnimations.isEmpty()) startanim = true; - _emojiAnimations.insert(_selected + 1, getms()); - } - } - setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default); - } - if (startanim && !_a_selected.animating()) _a_selected.start(); -} - -void EmojiColorPicker::drawVariant(Painter &p, int variant) { - float64 hover = _hovers[variant]; - - QPoint w(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding); - if (hover > 0) { - p.setOpacity(hover); - QPoint tl(w); - if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); - App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); - p.setOpacity(1); - } - int esize = EmojiSizes[EIndex + 1]; - p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_variants[variant]->x * esize, _variants[variant]->y * esize, esize, esize)); -} - -EmojiPanInner::EmojiPanInner() : TWidget() -, _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height) -, _a_selected(animation(this, &EmojiPanInner::step_selected)) -, _top(0) -, _selected(-1) -, _pressedSel(-1) -, _pickerSel(-1) { - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); - - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - setAttribute(Qt::WA_OpaquePaintEvent); - - _picker.hide(); - - _esize = EmojiSizes[EIndex + 1]; - - for (int32 i = 0; i < emojiTabCount; ++i) { - _counts[i] = emojiPackCount(emojiTabAtIndex(i)); - _hovers[i] = QVector(_counts[i], 0); - } - - _showPickerTimer.setSingleShot(true); - connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker())); - connect(&_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr))); - connect(&_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden())); -} - -void EmojiPanInner::setMaxHeight(int32 h) { - _maxHeight = h; - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); -} - -void EmojiPanInner::setScrollTop(int top) { - _top = top; -} - -int EmojiPanInner::countHeight() { - int result = 0; - for (int i = 0; i < emojiTabCount; ++i) { - int cnt = emojiPackCount(emojiTabAtIndex(i)), rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - result += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - - return result + st::emojiPanPadding; -} - -void EmojiPanInner::paintEvent(QPaintEvent *e) { - Painter p(this); - QRect r = e ? e->rect() : rect(); - if (r != rect()) { - p.setClipRect(r); - } - p.fillRect(r, st::white->b); - - int32 fromcol = floorclamp(r.x() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); - int32 tocol = ceilclamp(r.x() + r.width() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); - if (rtl()) { - qSwap(fromcol, tocol); - fromcol = EmojiPanPerRow - fromcol; - tocol = EmojiPanPerRow - tocol; - } - - int32 y, tilly = 0; - for (int c = 0; c < emojiTabCount; ++c) { - y = tilly; - int32 size = _counts[c]; - int32 rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); - tilly = y + st::emojiPanHeader + (rows * st::emojiPanSize.height()); - if (r.top() >= tilly) continue; - - y += st::emojiPanHeader; - if (_emojis[c].isEmpty()) { - _emojis[c] = emojiPack(emojiTabAtIndex(c)); - if (emojiTabAtIndex(c) != dbietRecent) { - for (EmojiPack::iterator i = _emojis[c].begin(), e = _emojis[c].end(); i != e; ++i) { - if ((*i)->color) { - EmojiColorVariants::const_iterator j = cEmojiVariants().constFind((*i)->code); - if (j != cEmojiVariants().cend()) { - EmojiPtr replace = emojiFromKey(j.value()); - if (replace) { - if (replace != TwoSymbolEmoji && replace->code == (*i)->code && replace->code2 == (*i)->code2) { - *i = replace; - } - } - } - } - } - } - } - - int32 fromrow = floorclamp(r.y() - y, st::emojiPanSize.height(), 0, rows); - int32 torow = ceilclamp(r.y() + r.height() - y, st::emojiPanSize.height(), 0, rows); - for (int32 i = fromrow; i < torow; ++i) { - for (int32 j = fromcol; j < tocol; ++j) { - int32 index = i * EmojiPanPerRow + j; - if (index >= size) break; - - float64 hover = (!_picker.isHidden() && c * MatrixRowShift + index == _pickerSel) ? 1 : _hovers[c][index]; - - QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height()); - if (hover > 0) { - p.setOpacity(hover); - QPoint tl(w); - if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); - App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); - p.setOpacity(1); - } - p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (_esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (_esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_emojis[c][index]->x * _esize, _emojis[c][index]->y * _esize, _esize, _esize)); - } - } - } -} - -bool EmojiPanInner::checkPickerHide() { - if (!_picker.isHidden() && _selected == _pickerSel) { - _picker.hideStart(); - _pickerSel = -1; - updateSelected(); - return true; - } - return false; -} - -void EmojiPanInner::mousePressEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); - if (checkPickerHide()) { - return; - } - _pressedSel = _selected; - - if (_selected >= 0) { - int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; - if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { - _pickerSel = _selected; - setCursor(style::cur_default); - if (cEmojiVariants().constFind(_emojis[tab][sel]->code) == cEmojiVariants().cend()) { - onShowPicker(); - } else { - _showPickerTimer.start(500); - } - } - } -} - -void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { - int32 pressed = _pressedSel; - _pressedSel = -1; - - _lastMousePos = e->globalPos(); - if (!_picker.isHidden()) { - if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { - return _picker.mouseReleaseEvent(0); - } else if (_pickerSel >= 0) { - int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; - if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { - if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) { - _picker.hideStart(); - _pickerSel = -1; - } - } - } - } - updateSelected(); - - if (_showPickerTimer.isActive()) { - _showPickerTimer.stop(); - _pickerSel = -1; - _picker.hide(); - } - - if (_selected < 0 || _selected != pressed) return; - - if (_selected >= emojiTabCount * MatrixRowShift) { - return; - } - - int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; - if (sel < _emojis[tab].size()) { - EmojiPtr emoji(_emojis[tab][sel]); - if (emoji->color && !_picker.isHidden()) return; - - selectEmoji(emoji); - } -} - -void EmojiPanInner::selectEmoji(EmojiPtr emoji) { - RecentEmojiPack &recent(cGetRecentEmojis()); - RecentEmojiPack::iterator i = recent.begin(), e = recent.end(); - for (; i != e; ++i) { - if (i->first == emoji) { - ++i->second; - if (i->second > 0x8000) { - for (RecentEmojiPack::iterator j = recent.begin(); j != e; ++j) { - if (j->second > 1) { - j->second /= 2; - } else { - j->second = 1; - } - } - } - for (; i != recent.begin(); --i) { - if ((i - 1)->second > i->second) { - break; - } - qSwap(*i, *(i - 1)); - } - break; - } - } - if (i == e) { - while (recent.size() >= EmojiPanPerRow * EmojiPanRowsPerPage) recent.pop_back(); - recent.push_back(qMakePair(emoji, 1)); - for (i = recent.end() - 1; i != recent.begin(); --i) { - if ((i - 1)->second > i->second) { - break; - } - qSwap(*i, *(i - 1)); - } - } - emit saveConfigDelayed(SaveRecentEmojisTimeout); - - emit selected(emoji); -} - -void EmojiPanInner::onShowPicker() { - if (_pickerSel < 0) return; - - int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; - if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { - int32 y = 0; - for (int c = 0; c <= tab; ++c) { - int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + (rows * st::emojiPanSize.height()); - } - y -= _picker.height() - st::buttonRadius + _top; - if (y < 0) { - y += _picker.height() - st::buttonRadius + st::emojiPanSize.height() - st::buttonRadius; - } - int xmax = width() - _picker.width(); - float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1); - if (rtl()) coef = 1. - coef; - _picker.move(qRound(xmax * coef), y); - - _picker.showEmoji(_emojis[tab][sel]->code); - emit disableScroll(true); - } -} - -void EmojiPanInner::onPickerHidden() { - _pickerSel = -1; - update(); - emit disableScroll(false); - - _lastMousePos = QCursor::pos(); - updateSelected(); -} - -QRect EmojiPanInner::emojiRect(int tab, int sel) { - int x = 0, y = 0; - for (int i = 0; i < emojiTabCount; ++i) { - if (i == tab) { - int rows = (sel / EmojiPanPerRow); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - x = st::emojiPanPadding + ((sel % EmojiPanPerRow) * st::emojiPanSize.width()); - break; - } else { - int cnt = _counts[i]; - int rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - } - return QRect(x, y, st::emojiPanSize.width(), st::emojiPanSize.height()); -} - -void EmojiPanInner::onColorSelected(EmojiPtr emoji) { - if (emoji->color) { - cRefEmojiVariants().insert(emoji->code, emojiKey(emoji)); - } - if (_pickerSel >= 0) { - int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; - if (tab >= 0 && tab < emojiTabCount) { - _emojis[tab][sel] = emoji; - rtlupdate(emojiRect(tab, sel)); - } - } - selectEmoji(emoji); - _picker.hideStart(); -} - -void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - if (!_picker.isHidden()) { - if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { - return _picker.mouseMoveEvent(0); - } else { - _picker.clearSelection(); - } - } - updateSelected(); -} - -void EmojiPanInner::leaveEvent(QEvent *e) { - clearSelection(); -} - -void EmojiPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { - clearSelection(); -} - -void EmojiPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { - _lastMousePos = QCursor::pos(); - updateSelected(); -} - -void EmojiPanInner::clearSelection(bool fast) { - _lastMousePos = mapToGlobal(QPoint(-10, -10)); - if (fast) { - for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _hovers[tab][sel] = 0; - } - _animations.clear(); - if (_selected >= 0) { - int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _hovers[tab][sel] = 0; - } - if (_pressedSel >= 0) { - int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _hovers[tab][sel] = 0; - } - _selected = _pressedSel = -1; - _a_selected.stop(); - } else { - updateSelected(); - } -} - -DBIEmojiTab EmojiPanInner::currentTab(int yOffset) const { - int y, ytill = 0; - for (int c = 0; c < emojiTabCount; ++c) { - int cnt = _counts[c]; - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); - if (yOffset < ytill) { - return emojiTabAtIndex(c); - } - } - return emojiTabAtIndex(emojiTabCount - 1); -} - -void EmojiPanInner::hideFinish() { - if (!_picker.isHidden()) { - _picker.hideStart(true); - _pickerSel = -1; - clearSelection(true); - } -} - -void EmojiPanInner::refreshRecent() { - clearSelection(true); - _counts[0] = emojiPackCount(dbietRecent); - if (_hovers[0].size() != _counts[0]) _hovers[0] = QVector(_counts[0], 0); - _emojis[0] = emojiPack(dbietRecent); - int32 h = countHeight(); - if (h != height()) { - resize(width(), h); - emit needRefreshPanels(); - } -} - -void EmojiPanInner::fillPanels(QVector &panels) { - if (_picker.parentWidget() != parentWidget()) { - _picker.setParent(parentWidget()); - } - for (int32 i = 0; i < panels.size(); ++i) { - panels.at(i)->hide(); - panels.at(i)->deleteLater(); - } - panels.clear(); - - int y = 0; - panels.reserve(emojiTabCount); - for (int c = 0; c < emojiTabCount; ++c) { - panels.push_back(new EmojiPanel(parentWidget(), lang(LangKey(lng_emoji_category0 + c)), Stickers::NoneSetId, true, y)); - connect(panels.back(), SIGNAL(mousePressed()), this, SLOT(checkPickerHide())); - int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - panels.back()->show(); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - _picker.raise(); -} - -void EmojiPanInner::refreshPanels(QVector &panels) { - if (panels.size() != emojiTabCount) return fillPanels(panels); - - int32 y = 0; - for (int c = 0; c < emojiTabCount; ++c) { - panels.at(c)->setWantedY(y); - int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } -} - -void EmojiPanInner::updateSelected() { - if (_pressedSel >= 0 || _pickerSel >= 0) return; - - int32 selIndex = -1; - QPoint p(mapFromGlobal(_lastMousePos)); - int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::emojiPanPadding; - for (int c = 0; c < emojiTabCount; ++c) { - int cnt = _counts[c]; - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); - if (p.y() >= y && p.y() < ytill) { - y += st::emojiPanHeader; - if (p.y() >= y && sx >= 0 && sx < EmojiPanPerRow * st::emojiPanSize.width()) { - selIndex = qFloor((p.y() - y) / st::emojiPanSize.height()) * EmojiPanPerRow + qFloor(sx / st::emojiPanSize.width()); - if (selIndex >= _emojis[c].size()) { - selIndex = -1; - } else { - selIndex += c * MatrixRowShift; - } - } - break; - } - } - - bool startanim = false; - int oldSel = _selected, newSel = selIndex; - - if (newSel != oldSel) { - if (oldSel >= 0) { - _animations.remove(oldSel + 1); - if (_animations.find(-oldSel - 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(-oldSel - 1, getms()); - } - } - if (newSel >= 0) { - _animations.remove(-newSel - 1); - if (_animations.find(newSel + 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(newSel + 1, getms()); - } - } - setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default); - if (newSel >= 0 && !_picker.isHidden()) { - if (newSel != _pickerSel) { - _picker.hideStart(); - } else { - _picker.showStart(); - } - } - } - - _selected = selIndex; - if (startanim && !_a_selected.animating()) _a_selected.start(); -} - -void EmojiPanInner::step_selected(uint64 ms, bool timer) { - QRegion toUpdate; - for (Animations::iterator i = _animations.begin(); i != _animations.end();) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (dt >= 1) { - _hovers[tab][sel] = (i.key() > 0) ? 1 : 0; - i = _animations.erase(i); - } else { - _hovers[tab][sel] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - toUpdate += emojiRect(tab, sel); - } - if (timer) rtlupdate(toUpdate.boundingRect()); - if (_animations.isEmpty()) _a_selected.stop(); -} - -void InlineCacheEntry::clearResults() { - for_const (const InlineBots::Result *result, results) { - delete result; - } - results.clear(); -} - -void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { - clearSelection(true); - - refreshRecent(); - - int32 y = 0; - for (int c = 0; c < emojiTabCount; ++c) { - if (emojiTabAtIndex(c) == packIndex) break; - int rows = (_counts[c] / EmojiPanPerRow) + ((_counts[c] % EmojiPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::emojiPanSize.height(); - } - - emit scrollToY(y); - - _lastMousePos = QCursor::pos(); - - update(); -} - -StickerPanInner::StickerPanInner() : TWidget() -, _a_selected(animation(this, &StickerPanInner::step_selected)) -, _top(0) -, _showingSavedGifs(cShowingSavedGifs()) -, _showingInlineItems(_showingSavedGifs) -, _setGifCommand(false) -, _lastScrolled(0) -, _inlineWithThumb(false) -, _selected(-1) -, _pressedSel(-1) -, _settings(this, lang(lng_stickers_you_have)) -, _previewShown(false) { - setMaxHeight(st::emojiPanMaxHeight - st::rbEmoji.height); - - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - setAttribute(Qt::WA_OpaquePaintEvent); - - connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); - connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); - - _previewTimer.setSingleShot(true); - connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); - - _updateInlineItems.setSingleShot(true); - connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems())); -} - -void StickerPanInner::setMaxHeight(int32 h) { - _maxHeight = h; - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); - _settings.moveToLeft((st::emojiPanWidth - _settings.width()) / 2, height() / 3); -} - -void StickerPanInner::setScrollTop(int top) { - if (top == _top) return; - - _lastScrolled = getms(); - _top = top; -} - -int32 StickerPanInner::countHeight(bool plain) { - int result = 0, minLastH = plain ? 0 : (_maxHeight - st::stickerPanPadding); - if (_showingInlineItems) { - result = st::emojiPanHeader; - if (_switchPmButton) { - result += _switchPmButton->height() + st::inlineResultsSkip; - } - for (int i = 0, l = _inlineRows.count(); i < l; ++i) { - result += _inlineRows.at(i).height; - } - } else { - for (int i = 0; i < _sets.size(); ++i) { - int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); - if (i == _sets.size() - 1 && h < minLastH) h = minLastH; - result += h; - } - } - return qMax(minLastH, result) + st::stickerPanPadding; -} - -StickerPanInner::~StickerPanInner() { - clearInlineRows(true); - deleteUnusedGifLayouts(); - deleteUnusedInlineLayouts(); -} - -QRect StickerPanInner::stickerRect(int tab, int sel) { - int x = 0, y = 0; - for (int i = 0; i < _sets.size(); ++i) { - if (i == tab) { - int rows = (((sel >= _sets.at(i).pack.size()) ? (sel - _sets.at(i).pack.size()) : sel) / StickerPanPerRow); - y += st::emojiPanHeader + rows * st::stickerPanSize.height(); - x = st::stickerPanPadding + ((sel % StickerPanPerRow) * st::stickerPanSize.width()); - break; - } else { - int cnt = _sets.at(i).pack.size(); - int rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::stickerPanSize.height(); - } - } - return QRect(x, y, st::stickerPanSize.width(), st::stickerPanSize.height()); -} - -void StickerPanInner::paintEvent(QPaintEvent *e) { - Painter p(this); - QRect r = e ? e->rect() : rect(); - if (r != rect()) { - p.setClipRect(r); - } - p.fillRect(r, st::white); - - if (_showingInlineItems) { - paintInlineItems(p, r); - } else { - paintStickers(p, r); - } -} - -void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) { - if (_inlineRows.isEmpty()) { - p.setFont(st::normalFont); - p.setPen(st::noContactsColor); - p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), lang(lng_inline_bot_no_results), style::al_center); - return; - } - InlineBots::Layout::PaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false); - - int top = st::emojiPanHeader; - if (_switchPmButton) { - top += _switchPmButton->height() + st::inlineResultsSkip; - } - - int fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width()); - for (int row = 0, rows = _inlineRows.size(); row < rows; ++row) { - const InlineRow &inlineRow(_inlineRows.at(row)); - if (top >= r.top() + r.height()) break; - if (top + inlineRow.height > r.top()) { - int left = st::inlineResultsLeft; - if (row == rows - 1) context.lastRow = true; - for (int col = 0, cols = inlineRow.items.size(); col < cols; ++col) { - if (left >= tox) break; - - const InlineItem *item = inlineRow.items.at(col); - int w = item->width(); - if (left + w > fromx) { - p.translate(left, top); - item->paint(p, r.translated(-left, -top), &context); - p.translate(-left, -top); - } - left += w; - if (item->hasRightSkip()) { - left += st::inlineResultsSkip; - } - } - } - top += inlineRow.height; - } -} - -void StickerPanInner::paintStickers(Painter &p, const QRect &r) { - int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); - int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); - if (rtl()) { - qSwap(fromcol, tocol); - fromcol = StickerPanPerRow - fromcol; - tocol = StickerPanPerRow - tocol; - } - - int32 y, tilly = 0; - for (int c = 0, l = _sets.size(); c < l; ++c) { - y = tilly; - int32 size = _sets.at(c).pack.size(); - int32 rows = (size / StickerPanPerRow) + ((size % StickerPanPerRow) ? 1 : 0); - tilly = y + st::emojiPanHeader + (rows * st::stickerPanSize.height()); - if (r.top() >= tilly) continue; - - bool special = (_sets[c].flags & MTPDstickerSet::Flag::f_official); - y += st::emojiPanHeader; - - int32 fromrow = floorclamp(r.y() - y, st::stickerPanSize.height(), 0, rows); - int32 torow = ceilclamp(r.y() + r.height() - y, st::stickerPanSize.height(), 0, rows); - for (int32 i = fromrow; i < torow; ++i) { - for (int32 j = fromcol; j < tocol; ++j) { - int32 index = i * StickerPanPerRow + j; - if (index >= size) break; - - float64 hover = _sets[c].hovers[index]; - - DocumentData *sticker = _sets[c].pack[index]; - if (!sticker->sticker()) continue; - - QPoint pos(st::stickerPanPadding + j * st::stickerPanSize.width(), y + i * st::stickerPanSize.height()); - if (hover > 0) { - p.setOpacity(hover); - QPoint tl(pos); - if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width()); - App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); - p.setOpacity(1); - } - - bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); - if (goodThumb) { - sticker->thumb->load(); - } else { - sticker->checkSticker(); - } - - float64 coef = qMin((st::stickerPanSize.width() - st::buttonRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::buttonRadius * 2) / float64(sticker->dimensions.height())); - if (coef > 1) coef = 1; - int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height()); - if (w < 1) w = 1; - if (h < 1) h = 1; - QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); - if (goodThumb) { - p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h)); - } else if (!sticker->sticker()->img->isNull()) { - p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h)); - } - - if (hover > 0 && _sets[c].id == Stickers::RecentSetId && _custom.at(index)) { - float64 xHover = _sets[c].hovers[_sets[c].pack.size() + index]; - - QPoint xPos = pos + QPoint(st::stickerPanSize.width() - st::stickerPanDelete.pxWidth(), 0); - p.setOpacity(hover * (xHover + (1 - xHover) * st::stickerPanDeleteOpacity)); - p.drawSpriteLeft(xPos, width(), st::stickerPanDelete); - p.setOpacity(1); - } - } - } - } -} - -void StickerPanInner::mousePressEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); - - _pressedSel = _selected; - ClickHandler::pressed(); - _previewTimer.start(QApplication::startDragTime()); -} - -void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { - _previewTimer.stop(); - - int32 pressed = _pressedSel; - _pressedSel = -1; - - ClickHandlerPtr activated = ClickHandler::unpressed(); - - _lastMousePos = e->globalPos(); - updateSelected(); - - if (_previewShown) { - _previewShown = false; - return; - } - - if (_selected < 0 || _selected != pressed || (_showingInlineItems && !activated)) return; - if (_showingInlineItems) { - if (!dynamic_cast(activated.data())) { - App::activateClickHandler(activated, e->button()); - return; - } - int row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; - if (row >= _inlineRows.size() || col >= _inlineRows.at(row).items.size()) { - return; - } - - InlineItem *item = _inlineRows.at(row).items.at(col); - if (PhotoData *photo = item->getPhoto()) { - if (photo->medium->loaded() || photo->thumb->loaded()) { - emit selected(photo); - } else if (!photo->medium->loading()) { - photo->thumb->loadEvenCancelled(); - photo->medium->loadEvenCancelled(); - } - } else if (DocumentData *document = item->getDocument()) { - if (document->loaded()) { - emit selected(document); - } else if (document->loading()) { - document->cancel(); - } else { - DocumentOpenClickHandler::doOpen(document, ActionOnLoadNone); - } - } else if (InlineResult *inlineResult = item->getResult()) { - if (inlineResult->onChoose(item)) { - emit selected(inlineResult, _inlineBot); - } - } - return; - } - if (_selected >= MatrixRowShift * _sets.size()) { - return; - } - - int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; - if (_sets[tab].id == Stickers::RecentSetId && sel >= _sets[tab].pack.size() && sel < _sets[tab].pack.size() * 2 && _custom.at(sel - _sets[tab].pack.size())) { - clearSelection(true); - bool refresh = false; - DocumentData *sticker = _sets[tab].pack.at(sel - _sets[tab].pack.size()); - RecentStickerPack &recent(cGetRecentStickers()); - for (int32 i = 0, l = recent.size(); i < l; ++i) { - if (recent.at(i).first == sticker) { - recent.removeAt(i); - Local::writeUserSettings(); - refresh = true; - break; - } - } - auto &sets = Global::RefStickerSets(); - auto it = sets.find(Stickers::CustomSetId); - if (it != sets.cend()) { - for (int32 i = 0, l = it->stickers.size(); i < l; ++i) { - if (it->stickers.at(i) == sticker) { - it->stickers.removeAt(i); - if (it->stickers.isEmpty()) { - sets.erase(it); - } - Local::writeInstalledStickers(); - refresh = true; - break; - } - } - } - if (refresh) { - refreshRecentStickers(); - updateSelected(); - update(); - } - return; - } - if (sel < _sets[tab].pack.size()) { - emit selected(_sets[tab].pack[sel]); - } -} - -void StickerPanInner::mouseMoveEvent(QMouseEvent *e) { - _lastMousePos = e->globalPos(); - updateSelected(); -} - -void StickerPanInner::leaveEvent(QEvent *e) { - clearSelection(); -} - -void StickerPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { - clearSelection(); -} - -void StickerPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { - _lastMousePos = QCursor::pos(); - updateSelected(); -} - -bool StickerPanInner::showSectionIcons() const { - return !inlineResultsShown(); -} - -void StickerPanInner::clearSelection(bool fast) { - _lastMousePos = mapToGlobal(QPoint(-10, -10)); - if (fast) { - if (_showingInlineItems) { - if (_selected >= 0) { - int32 srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift; - t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); - ClickHandler::clearActive(_inlineRows.at(srow).items.at(scol)); - setCursor(style::cur_default); - } - _selected = _pressedSel = -1; - return; - } - for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - _sets[tab].hovers[sel] = 0; - } - _animations.clear(); - if (_selected >= 0) { - int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - if (index >= 0 && tab < _sets.size() && _sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + _sets[tab].pack.size()) { - _sets[tab].hovers[sel] = 0; - sel -= _sets[tab].pack.size(); - } - _sets[tab].hovers[sel] = 0; - } - if (_pressedSel >= 0) { - int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - if (index >= 0 && tab < _sets.size() && _sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + _sets[tab].pack.size()) { - _sets[tab].hovers[sel] = 0; - sel -= _sets[tab].pack.size(); - } - _sets[tab].hovers[sel] = 0; - } - _selected = _pressedSel = -1; - _a_selected.stop(); - } else { - updateSelected(); - } -} - -void StickerPanInner::hideFinish(bool completely) { - if (completely) { - auto itemForget = [](const InlineItem *item) { - if (DocumentData *document = item->getDocument()) { - document->forget(); - } - if (PhotoData *photo = item->getPhoto()) { - photo->forget(); - } - if (InlineResult *result = item->getResult()) { - result->forget(); - } - }; - clearInlineRows(false); - for_const (InlineItem *item, _gifLayouts) { - itemForget(item); - } - for_const (InlineItem *item, _inlineLayouts) { - itemForget(item); - } - } - if (_setGifCommand && _showingSavedGifs) { - App::insertBotCommand(qsl(""), true); - } - _setGifCommand = false; -} - -void StickerPanInner::refreshStickers() { - clearSelection(true); - - _sets.clear(); - _sets.reserve(Global::StickerSetsOrder().size() + 1); - - refreshRecentStickers(false); - for (auto i = Global::StickerSetsOrder().cbegin(), e = Global::StickerSetsOrder().cend(); i != e; ++i) { - appendSet(*i); - } - - if (_showingInlineItems) { - _settings.hide(); - } else { - int32 h = countHeight(); - if (h != height()) resize(width(), h); - - _settings.setVisible(_sets.isEmpty()); - } - - - emit refreshIcons(); - - updateSelected(); -} - -bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) { - InlineItem *layout = nullptr; - if (savedGif) { - layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size()); - } else if (result) { - layout = layoutPrepareInlineResult(result, (_inlineRows.size() * MatrixRowShift) + row.items.size()); - } - if (!layout) return false; - - layout->preload(); - if (inlineRowFinalize(row, sumWidth, layout->isFullLine())) { - layout->setPosition(_inlineRows.size() * MatrixRowShift); - } - - sumWidth += layout->maxWidth(); - if (!row.items.isEmpty() && row.items.back()->hasRightSkip()) { - sumWidth += st::inlineResultsSkip; - } - - row.items.push_back(layout); - return true; -} - -bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) { - if (row.items.isEmpty()) return false; - - bool full = (row.items.size() >= InlineItemsMaxPerRow); - bool big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft); - if (full || big || force) { - _inlineRows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0)); - row = InlineRow(); - row.items.reserve(InlineItemsMaxPerRow); - sumWidth = 0; - return true; - } - return false; -} - -void StickerPanInner::refreshSavedGifs() { - if (_showingSavedGifs) { - _settings.hide(); - clearInlineRows(false); - if (_showingInlineItems) { - const SavedGifs &saved(cSavedGifs()); - if (saved.isEmpty()) { - showStickerSet(Stickers::RecentSetId); - return; - } else { - _inlineRows.reserve(saved.size()); - InlineRow row; - row.items.reserve(InlineItemsMaxPerRow); - int32 sumWidth = 0; - for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { - inlineRowsAddItem(*i, 0, row, sumWidth); - } - inlineRowFinalize(row, sumWidth, true); - } - deleteUnusedGifLayouts(); - - int32 h = countHeight(); - if (h != height()) resize(width(), h); - - update(); - } - } - emit refreshIcons(); - - updateSelected(); -} - -void StickerPanInner::inlineBotChanged() { - _setGifCommand = false; - refreshInlineRows(nullptr, nullptr, true); -} - -void StickerPanInner::clearInlineRows(bool resultsDeleted) { - if (resultsDeleted) { - if (_showingInlineItems) { - _selected = _pressedSel = -1; - } - } else { - if (_showingInlineItems) { - clearSelection(true); - } - for (InlineRows::const_iterator i = _inlineRows.cbegin(), e = _inlineRows.cend(); i != e; ++i) { - for (InlineItems::const_iterator j = i->items.cbegin(), end = i->items.cend(); j != end; ++j) { - (*j)->setPosition(-1); - } - } - } - _inlineRows.clear(); -} - -InlineItem *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) { - auto i = _gifLayouts.constFind(doc); - if (i == _gifLayouts.cend()) { - if (auto layout = InlineItem::createLayoutGif(doc)) { - i = _gifLayouts.insert(doc, layout.release()); - i.value()->initDimensions(); - } else { - return nullptr; - } - } - if (!i.value()->maxWidth()) return nullptr; - - i.value()->setPosition(position); - return i.value(); -} - -InlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *result, int32 position) { - auto i = _inlineLayouts.constFind(result); - if (i == _inlineLayouts.cend()) { - if (auto layout = InlineItem::createLayout(result, _inlineWithThumb)) { - i = _inlineLayouts.insert(result, layout.release()); - i.value()->initDimensions(); - } else { - return nullptr; - } - } - if (!i.value()->maxWidth()) return nullptr; - - i.value()->setPosition(position); - return i.value(); -} - -void StickerPanInner::deleteUnusedGifLayouts() { - if (_inlineRows.isEmpty() || !_showingSavedGifs) { // delete all - for_const (const InlineItem *item, _gifLayouts) { - delete item; - } - _gifLayouts.clear(); - } else { - for (GifLayouts::iterator i = _gifLayouts.begin(); i != _gifLayouts.cend();) { - if (i.value()->position() < 0) { - delete i.value(); - i = _gifLayouts.erase(i); - } else { - ++i; - } - } - } -} - -void StickerPanInner::deleteUnusedInlineLayouts() { - if (_inlineRows.isEmpty() || _showingSavedGifs) { // delete all - for_const (const InlineItem *item, _inlineLayouts) { - delete item; - } - _inlineLayouts.clear(); - } else { - for (InlineLayouts::iterator i = _inlineLayouts.begin(); i != _inlineLayouts.cend();) { - if (i.value()->position() < 0) { - delete i.value(); - i = _inlineLayouts.erase(i); - } else { - ++i; - } - } - } -} - -StickerPanInner::InlineRow &StickerPanInner::layoutInlineRow(InlineRow &row, int32 sumWidth) { - int32 count = row.items.size(); - t_assert(count <= InlineItemsMaxPerRow); - - // enumerate items in the order of growing maxWidth() - // for that sort item indices by maxWidth() - int indices[InlineItemsMaxPerRow]; - for (int i = 0; i < count; ++i) { - indices[i] = i; - } - std::sort(indices, indices + count, [&row](int a, int b) -> bool { - return row.items.at(a)->maxWidth() < row.items.at(b)->maxWidth(); - }); - - row.height = 0; - int availw = width() - st::inlineResultsLeft; - for (int i = 0; i < count; ++i) { - int index = indices[i]; - int w = sumWidth ? (row.items.at(index)->maxWidth() * availw / sumWidth) : row.items.at(index)->maxWidth(); - int actualw = qMax(w, int(st::inlineResultsMinWidth)); - row.height = qMax(row.height, row.items.at(index)->resizeGetHeight(actualw)); - if (sumWidth) { - availw -= actualw; - sumWidth -= row.items.at(index)->maxWidth(); - if (index > 0 && row.items.at(index - 1)->hasRightSkip()) { - availw -= st::inlineResultsSkip; - sumWidth -= st::inlineResultsSkip; - } - } - } - return row; -} - -void StickerPanInner::preloadImages() { - if (_showingInlineItems) { - for (int32 row = 0, rows = _inlineRows.size(); row < rows; ++row) { - for (int32 col = 0, cols = _inlineRows.at(row).items.size(); col < cols; ++col) { - _inlineRows.at(row).items.at(col)->preload(); - } - } - return; - } - - for (int32 i = 0, l = _sets.size(), k = 0; i < l; ++i) { - for (int32 j = 0, n = _sets.at(i).pack.size(); j < n; ++j) { - if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break; - - DocumentData *sticker = _sets.at(i).pack.at(j); - if (!sticker || !sticker->sticker()) continue; - - bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); - if (goodThumb) { - sticker->thumb->load(); - } else { - sticker->automaticLoad(0); - } - } - if (k > StickerPanPerRow * (StickerPanPerRow + 1)) break; - } -} - -uint64 StickerPanInner::currentSet(int yOffset) const { - if (_showingInlineItems) return Stickers::NoneSetId; - - int y, ytill = 0; - for (int i = 0, l = _sets.size(); i < l; ++i) { - int cnt = _sets.at(i).pack.size(); - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); - if (yOffset < ytill) { - return _sets.at(i).id; - } - } - return _sets.isEmpty() ? Stickers::RecentSetId : _sets.back().id; -} - -void StickerPanInner::hideInlineRowsPanel() { - clearInlineRows(false); - if (_showingInlineItems) { - _showingSavedGifs = cShowingSavedGifs(); - if (_showingSavedGifs) { - refreshSavedGifs(); - emit scrollToY(0); - emit scrollUpdated(); - } else { - showStickerSet(Stickers::RecentSetId); - } - } -} - -void StickerPanInner::clearInlineRowsPanel() { - clearInlineRows(false); -} - -void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) { - if (!entry || entry->switchPmText.isEmpty()) { - _switchPmButton.reset(); - _switchPmStartToken.clear(); - } else { - if (!_switchPmButton) { - _switchPmButton = std_::make_unique(this, QString(), st::switchPmButton); - _switchPmButton->show(); - _switchPmButton->move(st::inlineResultsLeft, st::emojiPanHeader); - connect(_switchPmButton.get(), SIGNAL(clicked()), this, SLOT(onSwitchPm())); - } - _switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper() - _switchPmStartToken = entry->switchPmStartToken; - } - update(); -} - -int StickerPanInner::refreshInlineRows(UserData *bot, const InlineCacheEntry *entry, bool resultsDeleted) { - _inlineBot = bot; - refreshSwitchPmButton(entry); - auto clearResults = [this, entry]() { - if (!entry) { - return true; - } - if (entry->results.isEmpty() && entry->switchPmText.isEmpty()) { - if (!_inlineBot || _inlineBot->username != cInlineGifBotUsername()) { - return true; - } - } - return false; - }; - if (clearResults()) { - if (resultsDeleted) { - clearInlineRows(true); - deleteUnusedInlineLayouts(); - } - emit emptyInlineRows(); - return 0; - } - - clearSelection(true); - - t_assert(_inlineBot != 0); - _inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username)); - - _showingInlineItems = true; - _showingSavedGifs = false; - _settings.hide(); - - int32 count = entry->results.size(), from = validateExistingInlineRows(entry->results), added = 0; - - if (count) { - _inlineRows.reserve(count); - InlineRow row; - row.items.reserve(InlineItemsMaxPerRow); - int32 sumWidth = 0; - for (int32 i = from; i < count; ++i) { - if (inlineRowsAddItem(0, entry->results.at(i), row, sumWidth)) { - ++added; - } - } - inlineRowFinalize(row, sumWidth, true); - } - - int32 h = countHeight(); - if (h != height()) resize(width(), h); - update(); - - emit refreshIcons(); - - _lastMousePos = QCursor::pos(); - updateSelected(); - - return added; -} - -int32 StickerPanInner::validateExistingInlineRows(const InlineResults &results) { - int32 count = results.size(), until = 0, untilrow = 0, untilcol = 0; - for (; until < count;) { - if (untilrow >= _inlineRows.size() || _inlineRows.at(untilrow).items.at(untilcol)->getResult() != results.at(until)) { - break; - } - ++until; - if (++untilcol == _inlineRows.at(untilrow).items.size()) { - ++untilrow; - untilcol = 0; - } - } - if (until == count) { // all items are layed out - if (untilrow == _inlineRows.size()) { // nothing changed - return until; - } - - for (int32 i = untilrow, l = _inlineRows.size(), skip = untilcol; i < l; ++i) { - for (int32 j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { - if (skip) { - --skip; - } else { - _inlineRows.at(i).items.at(j)->setPosition(-1); - } - } - } - if (!untilcol) { // all good rows are filled - _inlineRows.resize(untilrow); - return until; - } - _inlineRows.resize(untilrow + 1); - _inlineRows[untilrow].items.resize(untilcol); - _inlineRows[untilrow] = layoutInlineRow(_inlineRows[untilrow]); - return until; - } - if (untilrow && !untilcol) { // remove last row, maybe it is not full - --untilrow; - untilcol = _inlineRows.at(untilrow).items.size(); - } - until -= untilcol; - - for (int32 i = untilrow, l = _inlineRows.size(); i < l; ++i) { - for (int32 j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { - _inlineRows.at(i).items.at(j)->setPosition(-1); - } - } - _inlineRows.resize(untilrow); - - if (_inlineRows.isEmpty()) { - _inlineWithThumb = false; - for (int32 i = until; i < count; ++i) { - if (results.at(i)->hasThumbDisplay()) { - _inlineWithThumb = true; - break; - } - } - } - return until; -} - -void StickerPanInner::notify_inlineItemLayoutChanged(const InlineItem *layout) { - if (_selected < 0 || !_showingInlineItems) { - return; - } - - int32 row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; - if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { - if (layout == _inlineRows.at(row).items.at(col)) { - updateSelected(); - } - } -} - -void StickerPanInner::ui_repaintInlineItem(const InlineItem *layout) { - uint64 ms = getms(); - if (_lastScrolled + 100 <= ms) { - update(); - } else { - _updateInlineItems.start(_lastScrolled + 100 - ms); - } -} - -bool StickerPanInner::ui_isInlineItemVisible(const InlineItem *layout) { - int32 position = layout->position(); - if (!_showingInlineItems || position < 0) return false; - - int32 row = position / MatrixRowShift, col = position % MatrixRowShift; - t_assert((row < _inlineRows.size()) && (col < _inlineRows.at(row).items.size())); - - const InlineItems &inlineItems(_inlineRows.at(row).items); - int32 top = st::emojiPanHeader; - for (int32 i = 0; i < row; ++i) { - top += _inlineRows.at(i).height; - } - - return (top < _top + _maxHeight) && (top + _inlineRows.at(row).items.at(col)->height() > _top); -} - -bool StickerPanInner::ui_isInlineItemBeingChosen() { - return _showingInlineItems; -} - -void StickerPanInner::appendSet(uint64 setId) { - auto &sets = Global::StickerSets(); - auto it = sets.constFind(setId); - if (it == sets.cend() || (it->flags & MTPDstickerSet::Flag::f_archived) || it->stickers.isEmpty()) return; - - StickerPack pack; - pack.reserve(it->stickers.size()); - for (int32 i = 0, l = it->stickers.size(); i < l; ++i) { - pack.push_back(it->stickers.at(i)); - } - _sets.push_back(DisplayedSet(it->id, it->flags, it->title, pack.size() + 1, pack)); -} - -void StickerPanInner::refreshRecent() { - if (_showingInlineItems) { - if (_showingSavedGifs) { - refreshSavedGifs(); - } - } else { - refreshRecentStickers(); - } -} - -void StickerPanInner::refreshRecentStickers(bool performResize) { - _custom.clear(); - clearSelection(true); - auto &sets = Global::StickerSets(); - auto &recent = cGetRecentStickers(); - auto customIt = sets.constFind(Stickers::CustomSetId); - auto cloudIt = sets.constFind(Stickers::CloudRecentSetId); - if (recent.isEmpty() - && (customIt == sets.cend() || customIt->stickers.isEmpty()) - && (cloudIt == sets.cend() || cloudIt->stickers.isEmpty())) { - if (!_sets.isEmpty() && _sets.at(0).id == Stickers::RecentSetId) { - _sets.pop_front(); - } - } else { - StickerPack recentPack; - int customCnt = (customIt == sets.cend()) ? 0 : customIt->stickers.size(); - int cloudCnt = (cloudIt == sets.cend()) ? 0 : cloudIt->stickers.size(); - recentPack.reserve(cloudCnt + recent.size() + customCnt); - _custom.reserve(cloudCnt + recent.size() + customCnt); - if (cloudCnt > 0) { - for_const (auto sticker, cloudIt->stickers) { - recentPack.push_back(sticker); - _custom.push_back(false); - } - } - for_const (auto &recentSticker, recent) { - auto sticker = recentSticker.first; - recentPack.push_back(sticker); - _custom.push_back(false); - } - if (customCnt > 0) { - for_const (auto &sticker, customIt->stickers) { - auto index = recentPack.indexOf(sticker); - if (index >= cloudCnt) { - _custom[index] = true; // mark stickers from recent as custom - } else { - recentPack.push_back(sticker); - _custom.push_back(true); - } - } - } - if (_sets.isEmpty() || _sets.at(0).id != Stickers::RecentSetId) { - _sets.push_back(DisplayedSet(Stickers::RecentSetId, MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special, lang(lng_recent_stickers), recentPack.size() * 2, recentPack)); - } else { - _sets[0].pack = recentPack; - _sets[0].hovers.resize(recentPack.size() * 2); - } - } - - if (performResize && !_showingInlineItems) { - int32 h = countHeight(); - if (h != height()) { - resize(width(), h); - emit needRefreshPanels(); - } - - updateSelected(); - } -} - -void StickerPanInner::fillIcons(QList &icons) { - icons.clear(); - icons.reserve(_sets.size() + 1); - if (!cSavedGifs().isEmpty()) icons.push_back(StickerIcon(Stickers::NoneSetId)); - - if (_sets.isEmpty()) return; - int32 i = 0; - if (_sets.at(0).id == Stickers::RecentSetId) ++i; - if (i > 0) icons.push_back(StickerIcon(Stickers::RecentSetId)); - for (int32 l = _sets.size(); i < l; ++i) { - DocumentData *s = _sets.at(i).pack.at(0); - int32 availw = st::rbEmoji.width - 2 * st::stickerIconPadding, availh = st::rbEmoji.height - 2 * st::stickerIconPadding; - int32 thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1; - if (availw * thumbh > availh * thumbw) { - pixh = availh; - pixw = (pixh * thumbw) / thumbh; - } else { - pixw = availw; - pixh = thumbw ? ((pixw * thumbh) / thumbw) : 1; - } - if (pixw < 1) pixw = 1; - if (pixh < 1) pixh = 1; - icons.push_back(StickerIcon(_sets.at(i).id, s, pixw, pixh)); - } -} - -void StickerPanInner::fillPanels(QVector &panels) { - for (int32 i = 0; i < panels.size(); ++i) { - panels.at(i)->hide(); - panels.at(i)->deleteLater(); - } - panels.clear(); - - if (_showingInlineItems) { - panels.push_back(new EmojiPanel(parentWidget(), _showingSavedGifs ? lang(lng_saved_gifs) : _inlineBotTitle, Stickers::NoneSetId, true, 0)); - panels.back()->show(); - return; - } - - if (_sets.isEmpty()) return; - - int y = 0; - panels.reserve(_sets.size()); - for (int32 i = 0, l = _sets.size(); i < l; ++i) { - bool special = (_sets.at(i).flags & MTPDstickerSet::Flag::f_official); - panels.push_back(new EmojiPanel(parentWidget(), _sets.at(i).title, _sets.at(i).id, special, y)); - panels.back()->show(); - connect(panels.back(), SIGNAL(deleteClicked(quint64)), this, SIGNAL(removing(quint64))); - int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); - y += h; - } -} - -void StickerPanInner::refreshPanels(QVector &panels) { - if (_showingInlineItems) return; - - if (panels.size() != _sets.size()) return fillPanels(panels); - - int32 y = 0; - for (int32 i = 0, l = _sets.size(); i < l; ++i) { - panels.at(i)->setWantedY(y); - int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); - y += h; - } -} - -void StickerPanInner::updateSelected() { - if (_pressedSel >= 0 && !_previewShown) { - return; - } - - int32 selIndex = -1; - QPoint p(mapFromGlobal(_lastMousePos)); - - if (_showingInlineItems) { - int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft; - int sy = p.y() - st::emojiPanHeader; - if (_switchPmButton) { - sy -= _switchPmButton->height() + st::inlineResultsSkip; - } - int row = -1, col = -1, sel = -1; - ClickHandlerPtr lnk; - ClickHandlerHost *lnkhost = nullptr; - HistoryCursorState cursor = HistoryDefaultCursorState; - if (sy >= 0) { - row = 0; - for (int rows = _inlineRows.size(); row < rows; ++row) { - if (sy < _inlineRows.at(row).height) { - break; - } - sy -= _inlineRows.at(row).height; - } - } - if (sx >= 0 && row >= 0 && row < _inlineRows.size()) { - const InlineItems &inlineItems(_inlineRows.at(row).items); - col = 0; - for (int32 cols = inlineItems.size(); col < cols; ++col) { - int32 width = inlineItems.at(col)->width(); - if (sx < width) { - break; - } - sx -= width; - if (inlineItems.at(col)->hasRightSkip()) { - sx -= st::inlineResultsSkip; - } - } - if (col < inlineItems.size()) { - sel = row * MatrixRowShift + col; - inlineItems.at(col)->getState(lnk, cursor, sx, sy); - lnkhost = inlineItems.at(col); - } else { - row = col = -1; - } - } else { - row = col = -1; - } - int32 srow = (_selected >= 0) ? (_selected / MatrixRowShift) : -1; - int32 scol = (_selected >= 0) ? (_selected % MatrixRowShift) : -1; - if (_selected != sel) { - if (srow >= 0 && scol >= 0) { - t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); - Ui::repaintInlineItem(_inlineRows.at(srow).items.at(scol)); - } - _selected = sel; - if (row >= 0 && col >= 0) { - t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size()); - Ui::repaintInlineItem(_inlineRows.at(row).items.at(col)); - } - if (_pressedSel >= 0 && _selected >= 0 && _pressedSel != _selected) { - _pressedSel = _selected; - if (row >= 0 && col >= 0) { - auto layout = _inlineRows.at(row).items.at(col); - if (auto previewDocument = layout->getPreviewDocument()) { - Ui::showMediaPreview(previewDocument); - } else if (auto previewPhoto = layout->getPreviewPhoto()) { - Ui::showMediaPreview(previewPhoto); - } - } - } - } - if (ClickHandler::setActive(lnk, lnkhost)) { - setCursor(lnk ? style::cur_pointer : style::cur_default); - } - return; - } - - int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; - for (int c = 0, l = _sets.size(); c < l; ++c) { - const DisplayedSet &set(_sets.at(c)); - int cnt = set.pack.size(); - bool special = (set.flags & MTPDstickerSet::Flag::f_official); - - y = ytill; - ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); - if (p.y() >= y && p.y() < ytill) { - y += st::emojiPanHeader; - if (p.y() >= y && sx >= 0 && sx < StickerPanPerRow * st::stickerPanSize.width()) { - selIndex = qFloor((p.y() - y) / st::stickerPanSize.height()) * StickerPanPerRow + qFloor(sx / st::stickerPanSize.width()); - if (selIndex >= set.pack.size()) { - selIndex = -1; - } else { - if (set.id == Stickers::RecentSetId && _custom[selIndex]) { - int32 inx = sx - (selIndex % StickerPanPerRow) * st::stickerPanSize.width(), iny = p.y() - y - ((selIndex / StickerPanPerRow) * st::stickerPanSize.height()); - if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) { - selIndex += set.pack.size(); - } - } - selIndex += c * MatrixRowShift; - } - } - break; - } - } - - bool startanim = false; - int oldSel = _selected, oldSelTab = oldSel / MatrixRowShift, xOldSel = -1, newSel = selIndex, newSelTab = newSel / MatrixRowShift, xNewSel = -1; - if (oldSel >= 0 && oldSelTab < _sets.size() && _sets[oldSelTab].id == Stickers::RecentSetId && oldSel >= oldSelTab * MatrixRowShift + _sets[oldSelTab].pack.size()) { - xOldSel = oldSel; - oldSel -= _sets[oldSelTab].pack.size(); - } - if (newSel >= 0 && newSelTab < _sets.size() && _sets[newSelTab].id == Stickers::RecentSetId && newSel >= newSelTab * MatrixRowShift + _sets[newSelTab].pack.size()) { - xNewSel = newSel; - newSel -= _sets[newSelTab].pack.size(); - } - if (newSel != oldSel) { - if (oldSel >= 0) { - _animations.remove(oldSel + 1); - if (_animations.find(-oldSel - 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(-oldSel - 1, getms()); - } - } - if (newSel >= 0) { - _animations.remove(-newSel - 1); - if (_animations.find(newSel + 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(newSel + 1, getms()); - } - } - setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default); - } - if (xNewSel != xOldSel) { - if (xOldSel >= 0) { - _animations.remove(xOldSel + 1); - if (_animations.find(-xOldSel - 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(-xOldSel - 1, getms()); - } - } - if (xNewSel >= 0) { - _animations.remove(-xNewSel - 1); - if (_animations.find(xNewSel + 1) == _animations.end()) { - if (_animations.isEmpty()) startanim = true; - _animations.insert(xNewSel + 1, getms()); - } - } - } - _selected = selIndex; - if (_pressedSel >= 0 && _selected >= 0 && _pressedSel != _selected) { - _pressedSel = _selected; - if (newSel >= 0 && xNewSel < 0) { - Ui::showMediaPreview(_sets.at(newSelTab).pack.at(newSel % MatrixRowShift)); - } - } - if (startanim && !_a_selected.animating()) _a_selected.start(); -} - -void StickerPanInner::onSettings() { - Ui::showLayer(new StickersBox()); -} - -void StickerPanInner::onPreview() { - if (_pressedSel < 0) return; - if (_showingInlineItems) { - int32 row = _pressedSel / MatrixRowShift, col = _pressedSel % MatrixRowShift; - if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { - auto layout = _inlineRows.at(row).items.at(col); - if (auto previewDocument = layout->getPreviewDocument()) { - Ui::showMediaPreview(previewDocument); - _previewShown = true; - } else if (auto previewPhoto = layout->getPreviewPhoto()) { - Ui::showMediaPreview(previewPhoto); - _previewShown = true; - } - } - } else if (_pressedSel < MatrixRowShift * _sets.size()) { - int tab = (_pressedSel / MatrixRowShift), sel = _pressedSel % MatrixRowShift; - if (sel < _sets.at(tab).pack.size()) { - Ui::showMediaPreview(_sets.at(tab).pack.at(sel)); - _previewShown = true; - } - } -} - -void StickerPanInner::onUpdateInlineItems() { - if (!_showingInlineItems) return; - - uint64 ms = getms(); - if (_lastScrolled + 100 <= ms) { - update(); - } else { - _updateInlineItems.start(_lastScrolled + 100 - ms); - } -} - -void StickerPanInner::onSwitchPm() { - if (_inlineBot && _inlineBot->botInfo) { - _inlineBot->botInfo->startToken = _switchPmStartToken; - Ui::showPeerHistory(_inlineBot, ShowAndStartBotMsgId); - } -} - -void StickerPanInner::step_selected(uint64 ms, bool timer) { - QRegion toUpdate; - for (Animations::iterator i = _animations.begin(); i != _animations.end();) { - int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (dt >= 1) { - _sets[tab].hovers[sel] = (i.key() > 0) ? 1 : 0; - i = _animations.erase(i); - } else { - _sets[tab].hovers[sel] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - toUpdate += stickerRect(tab, sel); - } - if (timer) rtlupdate(toUpdate.boundingRect()); - if (_animations.isEmpty()) _a_selected.stop(); -} - -void StickerPanInner::showStickerSet(uint64 setId) { - clearSelection(true); - - if (setId == Stickers::NoneSetId) { - bool wasNotShowingGifs = !_showingInlineItems; - if (wasNotShowingGifs) { - _showingInlineItems = true; - _showingSavedGifs = true; - cSetShowingSavedGifs(true); - emit saveConfigDelayed(SaveRecentEmojisTimeout); - } - refreshSavedGifs(); - emit scrollToY(0); - emit scrollUpdated(); - showFinish(); - return; - } - - if (_showingInlineItems) { - if (_setGifCommand && _showingSavedGifs) { - App::insertBotCommand(qsl(""), true); - } - _setGifCommand = false; - - _showingInlineItems = false; - cSetShowingSavedGifs(false); - emit saveConfigDelayed(SaveRecentEmojisTimeout); - - Notify::clipStopperHidden(ClipStopperSavedGifsPanel); - - refreshRecentStickers(true); - emit refreshIcons(); - } - - int32 y = 0; - for (int c = 0; c < _sets.size(); ++c) { - if (_sets.at(c).id == setId) break; - int rows = (_sets[c].pack.size() / StickerPanPerRow) + ((_sets[c].pack.size() % StickerPanPerRow) ? 1 : 0); - y += st::emojiPanHeader + rows * st::stickerPanSize.height(); - } - - emit scrollToY(y); - emit scrollUpdated(); - - _lastMousePos = QCursor::pos(); - - update(); -} - -void StickerPanInner::updateShowingSavedGifs() { - if (cShowingSavedGifs()) { - if (!_showingInlineItems) { - clearSelection(true); - _showingSavedGifs = _showingInlineItems = true; - if (_inlineRows.isEmpty()) refreshSavedGifs(); - } - } else if (!_showingInlineItems) { - clearSelection(true); - _showingSavedGifs = false; - } -} - -void StickerPanInner::showFinish() { - if (_showingInlineItems && _showingSavedGifs) { - _setGifCommand = App::insertBotCommand('@' + cInlineGifBotUsername(), true); - } -} - -EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent) -, _wantedY(wantedY) -, _setId(setId) -, _special(special) -, _deleteVisible(false) -, _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // Stickers::NoneSetId if in emoji - resize(st::emojiPanWidth, st::emojiPanHeader); - setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); - setText(text); - if (_delete) { - _delete->hide(); - _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::notifyClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); - connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); - } -} - -void EmojiPanel::onDelete() { - emit deleteClicked(_setId); -} - -void EmojiPanel::setText(const QString &text) { - _fullText = text; - updateText(); -} - -void EmojiPanel::updateText() { - int32 availw = st::emojiPanWidth - st::emojiPanHeaderLeft * 2; - if (_deleteVisible) { - if (!_special && _setId != Stickers::NoneSetId) { - availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft; - } - } else { - auto switchText = ([this]() { - if (_setId != Stickers::NoneSetId) { - return lang(lng_switch_emoji); - } - if (cSavedGifs().isEmpty()) { - return lang(lng_switch_stickers); - } - return lang(lng_switch_stickers_gifs); - })(); - availw -= st::emojiSwitchSkip + st::emojiPanHeaderFont->width(switchText); - } - _text = st::emojiPanHeaderFont->elided(_fullText, availw); - update(); -} - -void EmojiPanel::setDeleteVisible(bool isVisible) { - if (_deleteVisible != isVisible) { - _deleteVisible = isVisible; - updateText(); - if (_delete) { - _delete->setVisible(_deleteVisible); - } - } -} - -void EmojiPanel::mousePressEvent(QMouseEvent *e) { - emit mousePressed(); -} - -void EmojiPanel::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_deleteVisible) { - p.fillRect(0, 0, width(), st::emojiPanHeader, st::emojiPanHeaderBg->b); - } - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiPanHeaderColor->p); - p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); -} - -EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent) -, _toStickers(toStickers) { - setCursor(style::cur_pointer); - updateText(); -} - -void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { - if (_toStickers) { - if (inlineBotUsername.isEmpty()) { - _text = lang(cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs); - } else { - _text = '@' + inlineBotUsername; - } - } else { - _text = lang(lng_switch_emoji); - } - _textWidth = st::emojiPanHeaderFont->width(_text); - if (_toStickers && !inlineBotUsername.isEmpty()) { - int32 maxw = 0; - for (int c = 0; c < emojiTabCount; ++c) { - maxw = qMax(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c)))); - } - maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - if (_textWidth > st::emojiPanWidth - maxw) { - _text = st::emojiPanHeaderFont->elided(_text, st::emojiPanWidth - maxw); - _textWidth = st::emojiPanHeaderFont->width(_text); - } - } - - int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); - resize(w, st::emojiPanHeader); -} - -void EmojiSwitchButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiSwitchColor->p); - if (_toStickers) { - p.drawTextRight(st::emojiSwitchSkip, st::emojiPanHeaderTop, width(), _text, _textWidth); - p.drawSpriteRight(QPoint(st::emojiSwitchImgSkip - st::emojiSwitchStickers.pxWidth(), (st::emojiPanHeader - st::emojiSwitchStickers.pxHeight()) / 2), width(), st::emojiSwitchStickers); - } else { - p.drawTextRight(st::emojiSwitchImgSkip - st::emojiSwitchEmoji.pxWidth(), st::emojiPanHeaderTop, width(), lang(lng_switch_emoji), _textWidth); - p.drawSpriteRight(QPoint(st::emojiSwitchSkip + _textWidth - st::emojiSwitchEmoji.pxWidth(), (st::emojiPanHeader - st::emojiSwitchEmoji.pxHeight()) / 2), width(), st::emojiSwitchEmoji); - } -} - -} // namespace internal - -EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) -, _maxHeight(st::emojiPanMaxHeight) -, _contentMaxHeight(st::emojiPanMaxHeight) -, _contentHeight(_contentMaxHeight) -, _contentHeightEmoji(_contentHeight - st::rbEmoji.height) -, _contentHeightStickers(_contentHeight - st::rbEmoji.height) -, _horizontal(false) -, _noTabUpdate(false) -, _hiding(false) -, a_opacity(0) -, _a_appearance(animation(this, &EmojiPan::step_appearance)) -, _shadow(st::dropdownDef.shadow) -, _recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent) -, _people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople) -, _nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature) -, _food(this , qsl("emoji_group"), dbietFood , QString(), false, st::rbEmojiFood) -, _activity(this, qsl("emoji_group"), dbietActivity, QString(), false, st::rbEmojiActivity) -, _travel(this , qsl("emoji_group"), dbietTravel , QString(), false, st::rbEmojiTravel) -, _objects(this , qsl("emoji_group"), dbietObjects , QString(), false, st::rbEmojiObjects) -, _symbols(this , qsl("emoji_group"), dbietSymbols , QString(), false, st::rbEmojiSymbols) -, _iconOver(-1) -, _iconSel(0) -, _iconDown(-1) -, _iconsDragging(false) -, _a_icons(animation(this, &EmojiPan::step_icons)) -, _iconsLeft(0) -, _iconsTop(0) -, _iconsStartX(0) -, _iconsMax(0) -, _iconsX(0, 0) -, _iconSelX(0, 0) -, _iconsStartAnim(0) -, _stickersShown(false) -, _shownFromInlineQuery(false) -, _a_slide(animation(this, &EmojiPan::step_slide)) -, e_scroll(this, st::emojiScroll) -, e_inner() -, e_switch(&e_scroll, true) -, s_scroll(this, st::emojiScroll) -, s_inner() -, s_switch(&s_scroll, false) -, _removingSetId(0) -, _inlineBot(0) -, _inlineRequestId(0) { - setFocusPolicy(Qt::NoFocus); - e_scroll.setFocusPolicy(Qt::NoFocus); - e_scroll.viewport()->setFocusPolicy(Qt::NoFocus); - s_scroll.setFocusPolicy(Qt::NoFocus); - s_scroll.viewport()->setFocusPolicy(Qt::NoFocus); - - _width = st::dropdownDef.padding.left() + st::emojiPanWidth + st::dropdownDef.padding.right(); - _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); - _bottom = 0; - resize(_width, _height); - - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); - - e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); - e_scroll.setWidget(&e_inner); - s_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); - s_scroll.setWidget(&s_inner); - - e_inner.moveToLeft(0, 0, e_scroll.width()); - s_inner.moveToLeft(0, 0, s_scroll.width()); - - int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2; - int32 top = _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; - prepareTab(left, top, _width, _recent); - prepareTab(left, top, _width, _people); - prepareTab(left, top, _width, _nature); - prepareTab(left, top, _width, _food); - prepareTab(left, top, _width, _activity); - prepareTab(left, top, _width, _travel); - prepareTab(left, top, _width, _objects); - prepareTab(left, top, _width, _symbols); - e_inner.fillPanels(e_panels); - updatePanelsPositions(e_panels, 0); - - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); - - connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int))); - connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); - - connect(&s_inner, SIGNAL(scrollToY(int)), &s_scroll, SLOT(scrollToY(int))); - connect(&s_inner, SIGNAL(scrollUpdated()), this, SLOT(onScrollStickers())); - - connect(&e_scroll, SIGNAL(scrolled()), this, SLOT(onScrollEmoji())); - connect(&s_scroll, SIGNAL(scrolled()), this, SLOT(onScrollStickers())); - - connect(&e_inner, SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr))); - connect(&s_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*))); - connect(&s_inner, SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*))); - connect(&s_inner, SIGNAL(selected(InlineBots::Result*,UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*))); - - connect(&s_inner, SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows())); - - connect(&s_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); - connect(&e_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); - s_switch.moveToRight(0, 0, st::emojiPanWidth); - e_switch.moveToRight(0, 0, st::emojiPanWidth); - - connect(&s_inner, SIGNAL(removing(quint64)), this, SLOT(onRemoveSet(quint64))); - connect(&s_inner, SIGNAL(refreshIcons()), this, SLOT(onRefreshIcons())); - connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); - connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); - - _saveConfigTimer.setSingleShot(true); - connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig())); - connect(&e_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); - connect(&s_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); - - // inline bots - _inlineRequestTimer.setSingleShot(true); - connect(&_inlineRequestTimer, SIGNAL(timeout()), this, SLOT(onInlineRequest())); - - if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { - connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); - } - - setMouseTracking(true); -// setAttribute(Qt::WA_AcceptTouchEvents); -} - -void EmojiPan::setMaxHeight(int32 h) { - _maxHeight = h; - updateContentHeight(); -} - -void EmojiPan::updateContentHeight() { - int32 h = qMin(_contentMaxHeight, _maxHeight); - int32 he = h - st::rbEmoji.height; - int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0); - if (h == _contentHeight && he == _contentHeightEmoji && hs == _contentHeightStickers) return; - - int32 was = _contentHeight, wase = _contentHeightEmoji, wass = _contentHeightStickers; - _contentHeight = h; - _contentHeightEmoji = he; - _contentHeightStickers = hs; - - _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); - - resize(_width, _height); - move(x(), _bottom - height()); - - if (was > _contentHeight || (was == _contentHeight && wass > _contentHeightStickers)) { - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); - s_inner.setMaxHeight(_contentHeightStickers); - e_inner.setMaxHeight(_contentHeightEmoji); - } else { - s_inner.setMaxHeight(_contentHeightStickers); - e_inner.setMaxHeight(_contentHeightEmoji); - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); - } - - _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; - _recent.move(_recent.x(), _iconsTop); - _people.move(_people.x(), _iconsTop); - _nature.move(_nature.x(), _iconsTop); - _food.move(_food.x(), _iconsTop); - _activity.move(_activity.x(), _iconsTop); - _travel.move(_travel.x(), _iconsTop); - _objects.move(_objects.x(), _iconsTop); - _symbols.move(_symbols.x(), _iconsTop); - - update(); -} - -void EmojiPan::prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab) { - tab.moveToLeft(left, top, _width); - left += tab.width(); - tab.setAttribute(Qt::WA_OpaquePaintEvent); - connect(&tab, SIGNAL(changed()), this, SLOT(onTabChange())); -} - -void EmojiPan::onWndActiveChanged() { - if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { - leaveEvent(0); - } -} - -void EmojiPan::onSaveConfig() { - Local::writeUserSettings(); -} - -void EmojiPan::onSaveConfigDelayed(int32 delay) { - _saveConfigTimer.start(delay); -} - -void EmojiPan::paintStickerSettingsIcon(Painter &p) const { - int settingsLeft = _iconsLeft + 7 * st::rbEmoji.width; - p.drawSpriteLeft(settingsLeft + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); - if (auto unread = Global::FeaturedStickerSetsUnreadCount()) { - Dialogs::Layout::UnreadBadgeStyle unreadSt; - unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersPanel; - unreadSt.size = st::stickersSettingsUnreadSize; - int unreadRight = settingsLeft + st::rbEmoji.width - st::stickersSettingsUnreadPosition.x(); - if (rtl()) unreadRight = width() - unreadRight; - int unreadTop = _iconsTop + st::stickersSettingsUnreadPosition.y(); - Dialogs::Layout::paintUnreadCount(p, QString::number(unread), unreadRight, unreadTop, unreadSt); - } -} - -void EmojiPan::paintEvent(QPaintEvent *e) { - Painter p(this); - - float64 o = 1; - if (!_cache.isNull()) { - p.setOpacity(o = a_opacity.current()); - } - - QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); - - _shadow.paint(p, r, st::dropdownDef.shadowShift); - - if (_toCache.isNull()) { - if (_cache.isNull()) { - p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::white->b); - if (_stickersShown && s_inner.showSectionIcons()) { - p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories); - paintStickerSettingsIcon(p); - - if (!_icons.isEmpty()) { - int32 x = _iconsLeft, i = 0, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); - for (int32 l = _icons.size(); i < l && !_icons.at(i).sticker; ++i) { - bool gifs = (_icons.at(i).setId == Stickers::NoneSetId); - if (selxrel != x) { - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsOver : st::rbEmojiRecent.imageRect); - } - if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) { - p.setOpacity(1 - (qAbs(selxrel - x) / st::rbEmoji.width)); - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsActive : st::rbEmojiRecent.chkImageRect); - p.setOpacity(1); - } - x += st::rbEmoji.width; - } - int32 skip = i; - - QRect clip(x, _iconsTop, _iconsLeft + 7 * st::rbEmoji.width - x, st::rbEmoji.height); - if (rtl()) clip.moveLeft(width() - x - clip.width()); - p.setClipRect(clip); - - i += _iconsX.current() / int(st::rbEmoji.width); - x -= _iconsX.current() % int(st::rbEmoji.width); - for (int32 l = qMin(_icons.size(), i + 8 - skip); i < l; ++i) { - const internal::StickerIcon &s(_icons.at(i)); - s.sticker->thumb->load(); - QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh)); - - p.drawPixmapLeft(x + (st::rbEmoji.width - s.pixw) / 2, _iconsTop + (st::rbEmoji.height - s.pixh) / 2, width(), pix); - x += st::rbEmoji.width; - } - - if (rtl()) selx = width() - selx - st::rbEmoji.width; - p.setOpacity(skip ? qMax(1., selx / float64(skip * st::rbEmoji.width)) : 1.); - p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor); - - float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.); - if (o_left > 0) { - p.setOpacity(o_left); - p.drawSpriteLeft(QRect(_iconsLeft + skip * st::rbEmoji.width, _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft); - } - float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.); - if (o_right > 0) { - p.setOpacity(o_right); - p.drawSpriteRight(QRect(width() - _iconsLeft - 7 * st::rbEmoji.width, _iconsTop, st::stickerIconRight.pxWidth(), st::rbEmoji.height), width(), st::stickerIconRight); - } - } - } else if (_stickersShown) { - int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); - p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::white); - } else { - p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), st::rbEmoji.height, st::emojiPanCategories); - int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); - p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::emojiPanCategories); - } - } else { - p.fillRect(r, st::white->b); - p.drawPixmap(r.left(), r.top(), _cache); - } - } else { - p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::rbEmoji.height), st::white->b); - p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height), st::emojiPanCategories->b); - p.setOpacity(o * a_fromAlpha.current()); - QRect fromDst = QRect(r.left() + a_fromCoord.current(), r.top(), _fromCache.width() / cIntRetinaFactor(), _fromCache.height() / cIntRetinaFactor()); - QRect fromSrc = QRect(0, 0, _fromCache.width(), _fromCache.height()); - if (fromDst.x() < r.left() + r.width() && fromDst.x() + fromDst.width() > r.left()) { - if (fromDst.x() < r.left()) { - fromSrc.setX((r.left() - fromDst.x()) * cIntRetinaFactor()); - fromDst.setX(r.left()); - } else if (fromDst.x() + fromDst.width() > r.left() + r.width()) { - fromSrc.setWidth((r.left() + r.width() - fromDst.x()) * cIntRetinaFactor()); - fromDst.setWidth(r.left() + r.width() - fromDst.x()); - } - p.drawPixmap(fromDst, _fromCache, fromSrc); - } - p.setOpacity(o * a_toAlpha.current()); - QRect toDst = QRect(r.left() + a_toCoord.current(), r.top(), _toCache.width() / cIntRetinaFactor(), _toCache.height() / cIntRetinaFactor()); - QRect toSrc = QRect(0, 0, _toCache.width(), _toCache.height()); - if (toDst.x() < r.left() + r.width() && toDst.x() + toDst.width() > r.left()) { - if (toDst.x() < r.left()) { - toSrc.setX((r.left() - toDst.x()) * cIntRetinaFactor()); - toDst.setX(r.left()); - } else if (toDst.x() + toDst.width() > r.left() + r.width()) { - toSrc.setWidth((r.left() + r.width() - toDst.x()) * cIntRetinaFactor()); - toDst.setWidth(r.left() + r.width() - toDst.x()); - } - p.drawPixmap(toDst, _toCache, toSrc); - } - } -} - -void EmojiPan::moveBottom(int32 bottom, bool force) { - _bottom = bottom; - if (isHidden() && !force) { - move(x(), _bottom - height()); - return; - } - if (_stickersShown && s_inner.inlineResultsShown()) { - moveToLeft(0, _bottom - height()); - } else { - moveToRight(0, _bottom - height()); - } -} - -void EmojiPan::enterEvent(QEvent *e) { - _hideTimer.stop(); - if (_hiding) showStart(); -} - -void EmojiPan::leaveEvent(QEvent *e) { - if (_removingSetId || s_inner.inlineResultsShown()) return; - if (_a_appearance.animating()) { - hideStart(); - } else { - _hideTimer.start(300); - } -} - -void EmojiPan::otherEnter() { - _hideTimer.stop(); - showStart(); -} - -void EmojiPan::otherLeave() { - if (_removingSetId || s_inner.inlineResultsShown()) return; - if (_a_appearance.animating()) { - hideStart(); - } else { - _hideTimer.start(0); - } -} - -void EmojiPan::mousePressEvent(QMouseEvent *e) { - if (!_stickersShown) return; - _iconsMousePos = e ? e->globalPos() : QCursor::pos(); - updateSelected(); - - if (_iconOver == _icons.size()) { - Ui::showLayer(new StickersBox()); - } else { - _iconDown = _iconOver; - _iconsMouseDown = _iconsMousePos; - _iconsStartX = _iconsX.current(); - } -} - -void EmojiPan::mouseMoveEvent(QMouseEvent *e) { - if (!_stickersShown) return; - _iconsMousePos = e ? e->globalPos() : QCursor::pos(); - updateSelected(); - - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - ++skip; - } - if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= skip) { - if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) { - _iconsDragging = true; - } - } - if (_iconsDragging) { - int32 newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax); - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); - _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _a_icons.stop(); - updateIcons(); - } - } -} - -void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { - if (!_stickersShown || _icons.isEmpty()) return; - - int32 wasDown = _iconDown; - _iconDown = -1; - - _iconsMousePos = e ? e->globalPos() : QCursor::pos(); - if (_iconsDragging) { - int32 newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax); - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); - _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _a_icons.stop(); - updateIcons(); - } - _iconsDragging = false; - updateSelected(); - } else { - updateSelected(); - - if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) { - _iconSelX = anim::ivalue(_iconOver * st::rbEmoji.width, _iconOver * st::rbEmoji.width); - s_inner.showStickerSet(_icons.at(_iconOver).setId); - } - } -} - -bool EmojiPan::event(QEvent *e) { - if (e->type() == QEvent::TouchBegin) { - - } else if (e->type() == QEvent::Wheel) { - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - ++skip; - } - if (!_icons.isEmpty() && _iconOver >= skip && _iconOver < _icons.size() && _iconDown < 0) { - QWheelEvent *ev = static_cast(e); - bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal); - bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical); - if (hor) _horizontal = true; - int32 newX = _iconsX.current(); - if (/*_horizontal && */hor) { - newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax); - } else if (/*!_horizontal && */ver) { - newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax); - } - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); - _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _a_icons.stop(); - updateSelected(); - updateIcons(); - } - } - } - return TWidget::event(e); -} - -void EmojiPan::fastHide() { - if (_a_appearance.animating()) { - _a_appearance.stop(); - } - a_opacity = anim::fvalue(0, 0); - _hideTimer.stop(); - hide(); - _cache = QPixmap(); -} - -void EmojiPan::refreshStickers() { - s_inner.refreshStickers(); - if (!_stickersShown) { - s_inner.preloadImages(); - } - update(); -} - -void EmojiPan::refreshSavedGifs() { - e_switch.updateText(); - e_switch.moveToRight(0, 0, st::emojiPanWidth); - s_inner.refreshSavedGifs(); - if (!_stickersShown) { - s_inner.preloadImages(); - } -} - -void EmojiPan::onRefreshIcons() { - _iconOver = -1; - _iconHovers.clear(); - _iconAnimations.clear(); - s_inner.fillIcons(_icons); - s_inner.fillPanels(s_panels); - _iconsX = anim::ivalue(0, 0); - _iconSelX.finish(); - _iconsStartAnim = 0; - _a_icons.stop(); - if (_icons.isEmpty()) { - _iconsMax = 0; - } else { - _iconHovers = QVector(_icons.size(), 0); - _iconsMax = qMax(int((_icons.size() - 7) * st::rbEmoji.width), 0); - } - updatePanelsPositions(s_panels, s_scroll.scrollTop()); - updateSelected(); - if (_stickersShown) { - validateSelectedIcon(); - updateContentHeight(); - } - updateIcons(); -} - -void EmojiPan::onRefreshPanels() { - s_inner.refreshPanels(s_panels); - e_inner.refreshPanels(e_panels); - if (_stickersShown) { - updatePanelsPositions(s_panels, s_scroll.scrollTop()); - } else { - updatePanelsPositions(e_panels, e_scroll.scrollTop()); - } -} - -void EmojiPan::leaveToChildEvent(QEvent *e, QWidget *child) { - if (!_stickersShown) return; - _iconsMousePos = QCursor::pos(); - updateSelected(); -} - -void EmojiPan::updateSelected() { - if (_iconDown >= 0) { - return; - } - - QPoint p(mapFromGlobal(_iconsMousePos)); - int32 x = p.x(), y = p.y(), newOver = -1; - if (rtl()) x = width() - x; - x -= _iconsLeft; - if (x >= st::rbEmoji.width * 7 && x < st::rbEmoji.width * 8 && y >= _iconsTop && y < _iconsTop + st::rbEmoji.height) { - newOver = _icons.size(); - } else if (!_icons.isEmpty()) { - if (y >= _iconsTop && y < _iconsTop + st::rbEmoji.height && x >= 0 && x < 7 * st::rbEmoji.width && x < _icons.size() * st::rbEmoji.width) { - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - if (x < st::rbEmoji.width) { - newOver = i; - break; - } - x -= st::rbEmoji.width; - ++skip; - } - if (newOver < 0) { - x += _iconsX.current(); - newOver = qFloor(x / st::rbEmoji.width) + skip; - } - } - } - if (newOver != _iconOver) { - if (newOver < 0) { - setCursor(style::cur_default); - } else if (_iconOver < 0) { - setCursor(style::cur_pointer); - } - bool startanim = false; - if (_iconOver >= 0 && _iconOver < _icons.size()) { - _iconAnimations.remove(_iconOver + 1); - if (_iconAnimations.find(-_iconOver - 1) == _iconAnimations.end()) { - if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; - _iconAnimations.insert(-_iconOver - 1, getms()); - } - } - _iconOver = newOver; - if (_iconOver >= 0 && _iconOver < _icons.size()) { - _iconAnimations.remove(-_iconOver - 1); - if (_iconAnimations.find(_iconOver + 1) == _iconAnimations.end()) { - if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; - _iconAnimations.insert(_iconOver + 1, getms()); - } - } - if (startanim && !_a_icons.animating()) _a_icons.start(); - } -} - -void EmojiPan::updateIcons() { - if (!_stickersShown || !s_inner.showSectionIcons()) return; - - QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); - update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); -} - -void EmojiPan::step_icons(uint64 ms, bool timer) { - if (!_stickersShown) { - _a_icons.stop(); - return; - } - - for (Animations::iterator i = _iconAnimations.begin(); i != _iconAnimations.end();) { - int index = qAbs(i.key()) - 1; - float64 dt = float64(ms - i.value()) / st::emojiPanDuration; - if (index >= _iconHovers.size()) { - i = _iconAnimations.erase(i); - } else if (dt >= 1) { - _iconHovers[index] = (i.key() > 0) ? 1 : 0; - i = _iconAnimations.erase(i); - } else { - _iconHovers[index] = (i.key() > 0) ? dt : (1 - dt); - ++i; - } - } - - if (_iconsStartAnim) { - float64 dt = (ms - _iconsStartAnim) / float64(st::stickerIconMove); - if (dt >= 1) { - _iconsStartAnim = 0; - _iconsX.finish(); - _iconSelX.finish(); - } else { - _iconsX.update(dt, anim::linear); - _iconSelX.update(dt, anim::linear); - } - if (timer) updateSelected(); - } - - if (timer) updateIcons(); - - if (_iconAnimations.isEmpty() && !_iconsStartAnim) { - _a_icons.stop(); - } -} - -void EmojiPan::step_slide(float64 ms, bool timer) { - float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration; - float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0; - if (dt2 >= 1) { - _a_slide.stop(); - a_fromCoord.finish(); - a_fromAlpha.finish(); - a_toCoord.finish(); - a_toAlpha.finish(); - _fromCache = _toCache = QPixmap(); - if (_cache.isNull()) showAll(); - } else { - a_fromCoord.update(dt1, st::introHideFunc); - a_fromAlpha.update(dt1, st::introAlphaHideFunc); - a_toCoord.update(dt2, st::introShowFunc); - a_toAlpha.update(dt2, st::introAlphaShowFunc); - } - if (timer) update(); -} - -void EmojiPan::step_appearance(float64 ms, bool timer) { - if (_cache.isNull()) { - _a_appearance.stop(); - return; - } - - float64 dt = ms / st::dropdownDef.duration; - if (dt >= 1) { - _a_appearance.stop(); - a_opacity.finish(); - if (_hiding) { - hideFinish(); - } else { - _cache = QPixmap(); - if (_toCache.isNull()) showAll(); - } - } else { - a_opacity.update(dt, anim::linear); - } - if (timer) update(); -} - -void EmojiPan::hideStart() { - if (_removingSetId || s_inner.inlineResultsShown()) return; - - hideAnimated(); -} - -void EmojiPan::prepareShowHideCache() { - if (_cache.isNull()) { - QPixmap from = _fromCache, to = _toCache; - _fromCache = _toCache = QPixmap(); - showAll(); - _cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); - _fromCache = from; _toCache = to; - } -} - -void EmojiPan::hideAnimated() { - if (_hiding) return; - - prepareShowHideCache(); - hideAll(); - _hiding = true; - a_opacity.start(0); - _a_appearance.start(); -} - -void EmojiPan::hideFinish() { - hide(); - e_inner.hideFinish(); - s_inner.hideFinish(true); - _cache = _toCache = _fromCache = QPixmap(); - _a_slide.stop(); - _horizontal = false; - _hiding = false; - - e_scroll.scrollToY(0); - if (!_recent.checked()) { - _noTabUpdate = true; - _recent.setChecked(true); - _noTabUpdate = false; - } - s_scroll.scrollToY(0); - _iconOver = _iconDown = -1; - _iconSel = 0; - _iconsX = anim::ivalue(0, 0); - _iconSelX = anim::ivalue(0, 0); - _iconsStartAnim = 0; - _a_icons.stop(); - _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); - _iconAnimations.clear(); - - Notify::clipStopperHidden(ClipStopperSavedGifsPanel); -} - -void EmojiPan::showStart() { - if (!isHidden() && !_hiding) { - return; - } - if (isHidden()) { - e_inner.refreshRecent(); - if (s_inner.inlineResultsShown() && refreshInlineRows()) { - _stickersShown = true; - _shownFromInlineQuery = true; - } else { - s_inner.refreshRecent(); - _stickersShown = false; - _shownFromInlineQuery = false; - _cache = QPixmap(); // clear after refreshInlineRows() - } - recountContentMaxHeight(); - s_inner.preloadImages(); - _fromCache = _toCache = QPixmap(); - _a_slide.stop(); - moveBottom(y() + height(), true); - } else if (_hiding) { - if (s_inner.inlineResultsShown() && refreshInlineRows()) { - onSwitch(); - } - } - prepareShowHideCache(); - hideAll(); - _hiding = false; - show(); - a_opacity.start(1); - _a_appearance.start(); - emit updateStickers(); -} - -bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { - if (e->type() == QEvent::Enter) { - //if (dynamic_cast(obj)) { - // enterEvent(e); - //} else { - otherEnter(); - //} - } else if (e->type() == QEvent::Leave) { - //if (dynamic_cast(obj)) { - // leaveEvent(e); - //} else { - otherLeave(); - //} - } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { - if (isHidden() || _hiding) { - _hideTimer.stop(); - showStart(); - } else { - hideAnimated(); - } - } - return false; -} - -void EmojiPan::stickersInstalled(uint64 setId) { - _stickersShown = true; - if (isHidden()) { - moveBottom(y() + height(), true); - show(); - a_opacity = anim::fvalue(0, 1); - a_opacity.update(0, anim::linear); - _cache = _fromCache = _toCache = QPixmap(); - } - showAll(); - s_inner.showStickerSet(setId); - updateContentHeight(); - showStart(); -} - -void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { - s_inner.notify_inlineItemLayoutChanged(layout); - } -} - -void EmojiPan::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { - s_inner.ui_repaintInlineItem(layout); - } -} - -bool EmojiPan::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { - return s_inner.ui_isInlineItemVisible(layout); - } - return false; -} - -bool EmojiPan::ui_isInlineItemBeingChosen() { - if (_stickersShown && !isHidden()) { - return s_inner.ui_isInlineItemBeingChosen(); - } - return false; -} - -void EmojiPan::showAll() { - if (_stickersShown) { - s_scroll.show(); - _recent.hide(); - _people.hide(); - _nature.hide(); - _food.hide(); - _activity.hide(); - _travel.hide(); - _objects.hide(); - _symbols.hide(); - e_scroll.hide(); - } else { - s_scroll.hide(); - _recent.show(); - _people.show(); - _nature.show(); - _food.show(); - _activity.show(); - _travel.show(); - _objects.show(); - _symbols.show(); - e_scroll.show(); - } -} - -void EmojiPan::hideAll() { - _recent.hide(); - _people.hide(); - _nature.hide(); - _food.hide(); - _activity.hide(); - _travel.hide(); - _objects.hide(); - _symbols.hide(); - e_scroll.hide(); - s_scroll.hide(); - e_inner.clearSelection(true); - s_inner.clearSelection(true); -} - -void EmojiPan::onTabChange() { - if (_noTabUpdate) return; - DBIEmojiTab newTab = dbietRecent; - if (_people.checked()) newTab = dbietPeople; - else if (_nature.checked()) newTab = dbietNature; - else if (_food.checked()) newTab = dbietFood; - else if (_activity.checked()) newTab = dbietActivity; - else if (_travel.checked()) newTab = dbietTravel; - else if (_objects.checked()) newTab = dbietObjects; - else if (_symbols.checked()) newTab = dbietSymbols; - e_inner.showEmojiPack(newTab); -} - -void EmojiPan::updatePanelsPositions(const QVector &panels, int32 st) { - for (int32 i = 0, l = panels.size(); i < l; ++i) { - int32 y = panels.at(i)->wantedY() - st; - if (y < 0) { - y = (i + 1 < l) ? qMin(panels.at(i + 1)->wantedY() - st - int(st::emojiPanHeader), 0) : 0; - } - panels.at(i)->move(0, y); - panels.at(i)->setDeleteVisible(y >= st::emojiPanHeader); - } -} - -void EmojiPan::onScrollEmoji() { - auto st = e_scroll.scrollTop(); - - updatePanelsPositions(e_panels, st); - - auto tab = e_inner.currentTab(st); - FlatRadiobutton *check = nullptr; - switch (tab) { - case dbietRecent: check = &_recent; break; - case dbietPeople: check = &_people; break; - case dbietNature: check = &_nature; break; - case dbietFood: check = &_food; break; - case dbietActivity: check = &_activity; break; - case dbietTravel: check = &_travel; break; - case dbietObjects: check = &_objects; break; - case dbietSymbols: check = &_symbols; break; - } - if (check && !check->checked()) { - _noTabUpdate = true; - check->setChecked(true); - _noTabUpdate = false; - } - - e_inner.setScrollTop(st); -} - -void EmojiPan::onScrollStickers() { - auto st = s_scroll.scrollTop(); - - updatePanelsPositions(s_panels, st); - - validateSelectedIcon(true); - if (st + s_scroll.height() > s_scroll.scrollTopMax()) { - onInlineRequest(); - } - - s_inner.setScrollTop(st); -} - -void EmojiPan::validateSelectedIcon(bool animated) { - uint64 setId = s_inner.currentSet(s_scroll.scrollTop()); - int32 newSel = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).setId == setId) { - newSel = i; - break; - } - } - if (newSel != _iconSel) { - _iconSel = newSel; - int32 skip = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).sticker) break; - ++skip; - } - if (animated) { - _iconSelX.start(newSel * st::rbEmoji.width); - } else { - _iconSelX = anim::ivalue(newSel * st::rbEmoji.width, newSel * st::rbEmoji.width); - } - _iconsX.start(snap((2 * newSel - 7 - skip) * int(st::rbEmoji.width) / 2, 0, _iconsMax)); - _iconsStartAnim = getms(); - _a_icons.start(); - updateSelected(); - updateIcons(); - } -} - -void EmojiPan::onSwitch() { - QPixmap cache = _cache; - _fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); - _stickersShown = !_stickersShown; - if (!_stickersShown) { - Notify::clipStopperHidden(ClipStopperSavedGifsPanel); - } else { - if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { - s_inner.showStickerSet(Stickers::DefaultSetId); - } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && Global::StickerSetsOrder().isEmpty()) { - s_inner.showStickerSet(Stickers::NoneSetId); - } else { - s_inner.updateShowingSavedGifs(); - } - if (cShowingSavedGifs()) { - s_inner.showFinish(); - } - validateSelectedIcon(); - updateContentHeight(); - } - _iconOver = -1; - _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); - _iconAnimations.clear(); - _a_icons.stop(); - - _cache = QPixmap(); - showAll(); - _toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); - _cache = cache; - - hideAll(); - - if (_stickersShown) { - e_inner.hideFinish(); - } else { - s_inner.hideFinish(false); - } - - a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanWidth, 0) : anim::ivalue(-st::emojiPanWidth, 0); - a_toAlpha = anim::fvalue(0, 1); - a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanWidth) : anim::ivalue(0, st::emojiPanWidth); - a_fromAlpha = anim::fvalue(1, 0); - - _a_slide.start(); - update(); -} - -void EmojiPan::onRemoveSet(quint64 setId) { - auto &sets = Global::StickerSets(); - auto it = sets.constFind(setId); - if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { - _removingSetId = it->id; - ConfirmBox *box = new ConfirmBox(lng_stickers_remove_pack(lt_sticker_pack, it->title), lang(lng_box_remove)); - connect(box, SIGNAL(confirmed()), this, SLOT(onRemoveSetSure())); - connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide())); - Ui::showLayer(box); - } -} - -void EmojiPan::onRemoveSetSure() { - Ui::hideLayer(); - auto &sets = Global::RefStickerSets(); - auto it = sets.find(_removingSetId); - if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { - if (it->id && it->access) { - MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)))); - } else if (!it->shortName.isEmpty()) { - MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetShortName(MTP_string(it->shortName)))); - } - bool writeRecent = false; - RecentStickerPack &recent(cGetRecentStickers()); - for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { - if (it->stickers.indexOf(i->first) >= 0) { - i = recent.erase(i); - writeRecent = true; - } else { - ++i; - } - } - it->flags &= ~MTPDstickerSet::Flag::f_installed; - if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { - sets.erase(it); - } - int removeIndex = Global::StickerSetsOrder().indexOf(_removingSetId); - if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); - refreshStickers(); - Local::writeInstalledStickers(); - if (writeRecent) Local::writeUserSettings(); - } - _removingSetId = 0; -} - -void EmojiPan::onDelayedHide() { - if (!rect().contains(mapFromGlobal(QCursor::pos()))) { - _hideTimer.start(3000); - } - _removingSetId = 0; -} - -void EmojiPan::clearInlineBot() { - inlineBotChanged(); - e_switch.updateText(); - e_switch.moveToRight(0, 0, st::emojiPanWidth); -} - -bool EmojiPan::hideOnNoInlineResults() { - return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); -} - -void EmojiPan::inlineBotChanged() { - if (!_inlineBot) return; - - if (!isHidden() && !_hiding) { - if (hideOnNoInlineResults() || !rect().contains(mapFromGlobal(QCursor::pos()))) { - hideAnimated(); - } - } - - if (_inlineRequestId) MTP::cancel(_inlineRequestId); - _inlineRequestId = 0; - _inlineQuery = _inlineNextQuery = _inlineNextOffset = QString(); - _inlineBot = nullptr; - for (InlineCache::const_iterator i = _inlineCache.cbegin(), e = _inlineCache.cend(); i != e; ++i) { - delete i.value(); - } - _inlineCache.clear(); - s_inner.inlineBotChanged(); - s_inner.hideInlineRowsPanel(); - - Notify::inlineBotRequesting(false); -} - -void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) { - _inlineRequestId = 0; - Notify::inlineBotRequesting(false); - - auto it = _inlineCache.find(_inlineQuery); - - bool adding = (it != _inlineCache.cend()); - if (result.type() == mtpc_messages_botResults) { - const auto &d(result.c_messages_botResults()); - const auto &v(d.vresults.c_vector().v); - uint64 queryId(d.vquery_id.v); - - if (!adding) { - it = _inlineCache.insert(_inlineQuery, new internal::InlineCacheEntry()); - } - it.value()->nextOffset = qs(d.vnext_offset); - if (d.has_switch_pm() && d.vswitch_pm.type() == mtpc_inlineBotSwitchPM) { - const auto &switchPm = d.vswitch_pm.c_inlineBotSwitchPM(); - it.value()->switchPmText = qs(switchPm.vtext); - it.value()->switchPmStartToken = qs(switchPm.vstart_param); - } - - if (int count = v.size()) { - it.value()->results.reserve(it.value()->results.size() + count); - } - int added = 0; - for_const (const auto &res, v) { - if (auto result = InlineBots::Result::create(queryId, res)) { - ++added; - it.value()->results.push_back(result.release()); - } - } - - if (!added) { - it.value()->nextOffset = QString(); - } - } else if (adding) { - it.value()->nextOffset = QString(); - } - - if (!showInlineRows(!adding)) { - it.value()->nextOffset = QString(); - } - onScrollStickers(); -} - -bool EmojiPan::inlineResultsFail(const RPCError &error) { - // show error? - Notify::inlineBotRequesting(false); - _inlineRequestId = 0; - return true; -} - -void EmojiPan::queryInlineBot(UserData *bot, PeerData *peer, QString query) { - bool force = false; - _inlineQueryPeer = peer; - if (bot != _inlineBot) { - inlineBotChanged(); - _inlineBot = bot; - force = true; - //if (_inlineBot->isBotInlineGeo()) { - // Ui::showLayer(new InformBox(lang(lng_bot_inline_geo_unavailable))); - //} - } - //if (_inlineBot && _inlineBot->isBotInlineGeo()) { - // return; - //} - - if (_inlineQuery != query || force) { - if (_inlineRequestId) { - MTP::cancel(_inlineRequestId); - _inlineRequestId = 0; - Notify::inlineBotRequesting(false); - } - if (_inlineCache.contains(query)) { - _inlineRequestTimer.stop(); - _inlineQuery = _inlineNextQuery = query; - showInlineRows(true); - } else { - _inlineNextQuery = query; - _inlineRequestTimer.start(InlineBotRequestDelay); - } - } -} - -void EmojiPan::onInlineRequest() { - if (_inlineRequestId || !_inlineBot || !_inlineQueryPeer) return; - _inlineQuery = _inlineNextQuery; - - QString nextOffset; - InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); - if (i != _inlineCache.cend()) { - nextOffset = i.value()->nextOffset; - if (nextOffset.isEmpty()) return; - } - Notify::inlineBotRequesting(true); - MTPmessages_GetInlineBotResults::Flags flags = 0; - _inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(MTP_flags(flags), _inlineBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); -} - -void EmojiPan::onEmptyInlineRows() { - if (_shownFromInlineQuery || hideOnNoInlineResults()) { - hideAnimated(); - s_inner.clearInlineRowsPanel(); - } else if (!_inlineBot) { - s_inner.hideInlineRowsPanel(); - } else { - s_inner.clearInlineRowsPanel(); - } -} - -bool EmojiPan::refreshInlineRows(int32 *added) { - auto i = _inlineCache.constFind(_inlineQuery); - const internal::InlineCacheEntry *entry = nullptr; - if (i != _inlineCache.cend()) { - if (!i.value()->results.isEmpty() || !i.value()->switchPmText.isEmpty()) { - entry = i.value(); - } - _inlineNextOffset = i.value()->nextOffset; - } - if (!entry) prepareShowHideCache(); - int32 result = s_inner.refreshInlineRows(_inlineBot, entry, false); - if (added) *added = result; - return (entry != nullptr); -} - -int32 EmojiPan::showInlineRows(bool newResults) { - int32 added = 0; - bool clear = !refreshInlineRows(&added); - if (newResults) s_scroll.scrollToY(0); - - e_switch.updateText(s_inner.inlineResultsShown() ? _inlineBot->username : QString()); - e_switch.moveToRight(0, 0, st::emojiPanWidth); - - bool hidden = isHidden(); - if (!hidden && !clear) { - recountContentMaxHeight(); - } - if (clear) { - if (!hidden && hideOnNoInlineResults()) { - hideAnimated(); - } else if (!_hiding) { - _cache = QPixmap(); // clear after refreshInlineRows() - } - } else { - _hideTimer.stop(); - if (hidden || _hiding) { - showStart(); - } else if (!_stickersShown) { - onSwitch(); - } - } - - return added; -} - -void EmojiPan::recountContentMaxHeight() { - if (_shownFromInlineQuery) { - _contentMaxHeight = qMin(s_inner.countHeight(true), int(st::emojiPanMaxHeight)); - } else { - _contentMaxHeight = st::emojiPanMaxHeight; - } - updateContentHeight(); -} diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index dfd81a7cf..836b2a531 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -27,7 +27,6 @@ class Dropdown : public TWidget { Q_OBJECT public: - Dropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef); IconedButton *addButton(IconedButton *button); @@ -61,11 +60,9 @@ public: } signals: - void hiding(); public slots: - void hideStart(); void hideFinish(); @@ -75,20 +72,19 @@ public slots: void buttonStateChanged(int oldState, ButtonStateChangeSource source); private: - void adjustButtons(); - bool _ignore; + bool _ignore = false; typedef QVector Buttons; Buttons _buttons; - int32 _selected; + int32 _selected = -1; const style::dropdown &_st; int32 _width, _height; - bool _hiding; + bool _hiding = false; anim::fvalue a_opacity; Animation _a_appearance; @@ -103,7 +99,6 @@ class DragArea : public TWidget { Q_OBJECT public: - DragArea(QWidget *parent); void paintEvent(QPaintEvent *e); @@ -133,18 +128,15 @@ public: } signals: - void dropped(const QMimeData *data); public slots: - void hideStart(); void hideFinish(); void showStart(); private: - bool _hiding, _in; anim::fvalue a_opacity; @@ -156,588 +148,3 @@ private: QString _text, _subtext; }; - -namespace InlineBots { -namespace Layout { -class ItemBase; -} // namespace Layout -class Result; -} // namespace InlineBots - -namespace internal { - -constexpr int InlineItemsMaxPerRow = 5; -constexpr int EmojiColorsCount = 5; - -using InlineResult = InlineBots::Result; -using InlineResults = QList; -using InlineItem = InlineBots::Layout::ItemBase; - -struct InlineCacheEntry { - ~InlineCacheEntry() { - clearResults(); - } - QString nextOffset; - QString switchPmText, switchPmStartToken; - InlineResults results; // owns this results list - void clearResults(); -}; - -class EmojiColorPicker : public TWidget { - Q_OBJECT - -public: - - EmojiColorPicker(); - - void showEmoji(uint32 code); - - void paintEvent(QPaintEvent *e); - void enterEvent(QEvent *e); - void leaveEvent(QEvent *e); - void mousePressEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - - void step_appearance(float64 ms, bool timer); - void step_selected(uint64 ms, bool timer); - void showStart(); - - void clearSelection(bool fast = false); - -public slots: - - void hideStart(bool fast = false); - -signals: - - void emojiSelected(EmojiPtr emoji); - void hidden(); - -private: - - void drawVariant(Painter &p, int variant); - - void updateSelected(); - - bool _ignoreShow; - - EmojiPtr _variants[EmojiColorsCount + 1]; - - typedef QMap EmojiAnimations; // index - showing, -index - hiding - EmojiAnimations _emojiAnimations; - Animation _a_selected; - - float64 _hovers[EmojiColorsCount + 1]; - - int32 _selected, _pressedSel; - QPoint _lastMousePos; - - bool _hiding; - QPixmap _cache; - - anim::fvalue a_opacity; - Animation _a_appearance; - - QTimer _hideTimer; - - BoxShadow _shadow; - -}; - -class EmojiPanel; -class EmojiPanInner : public TWidget { - Q_OBJECT - -public: - - EmojiPanInner(); - - void setMaxHeight(int32 h); - void paintEvent(QPaintEvent *e) override; - - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; - void leaveToChildEvent(QEvent *e, QWidget *child) override; - void enterFromChildEvent(QEvent *e, QWidget *child) override; - - void step_selected(uint64 ms, bool timer); - void hideFinish(); - - void showEmojiPack(DBIEmojiTab packIndex); - - void clearSelection(bool fast = false); - - DBIEmojiTab currentTab(int yOffset) const; - - void refreshRecent(); - - void setScrollTop(int top); - - void fillPanels(QVector &panels); - void refreshPanels(QVector &panels); - -public slots: - - void updateSelected(); - - void onShowPicker(); - void onPickerHidden(); - void onColorSelected(EmojiPtr emoji); - - bool checkPickerHide(); - -signals: - - void selected(EmojiPtr emoji); - - void switchToStickers(); - - void scrollToY(int y); - void disableScroll(bool dis); - - void needRefreshPanels(); - void saveConfigDelayed(int32 delay); - -private: - - int32 _maxHeight; - - int32 countHeight(); - void selectEmoji(EmojiPtr emoji); - - QRect emojiRect(int tab, int sel); - - typedef QMap Animations; // index - showing, -index - hiding - Animations _animations; - Animation _a_selected; - - int32 _top, _counts[emojiTabCount]; - - QVector _emojis[emojiTabCount]; - QVector _hovers[emojiTabCount]; - - int32 _esize; - - int32 _selected, _pressedSel, _pickerSel; - QPoint _lastMousePos; - - EmojiColorPicker _picker; - QTimer _showPickerTimer; -}; - -struct StickerIcon { - StickerIcon(uint64 setId) : setId(setId), sticker(0), pixw(0), pixh(0) { - } - StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) { - } - uint64 setId; - DocumentData *sticker; - int32 pixw, pixh; -}; - -class StickerPanInner : public TWidget { - Q_OBJECT - -public: - - StickerPanInner(); - - void setMaxHeight(int32 h); - void paintEvent(QPaintEvent *e) override; - - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; - void leaveToChildEvent(QEvent *e, QWidget *child) override; - void enterFromChildEvent(QEvent *e, QWidget *child) override; - - void step_selected(uint64 ms, bool timer); - - void hideFinish(bool completely); - void showFinish(); - void showStickerSet(uint64 setId); - void updateShowingSavedGifs(); - - bool showSectionIcons() const; - void clearSelection(bool fast = false); - - void refreshStickers(); - void refreshRecentStickers(bool resize = true); - void refreshSavedGifs(); - int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted); - void refreshRecent(); - void inlineBotChanged(); - void hideInlineRowsPanel(); - void clearInlineRowsPanel(); - - void fillIcons(QList &icons); - void fillPanels(QVector &panels); - void refreshPanels(QVector &panels); - - void setScrollTop(int top); - void preloadImages(); - - uint64 currentSet(int yOffset) const; - - void notify_inlineItemLayoutChanged(const InlineItem *layout); - void ui_repaintInlineItem(const InlineItem *layout); - bool ui_isInlineItemVisible(const InlineItem *layout); - bool ui_isInlineItemBeingChosen(); - - bool inlineResultsShown() const { - return _showingInlineItems && !_showingSavedGifs; - } - int32 countHeight(bool plain = false); - - ~StickerPanInner(); - -private slots: - - void updateSelected(); - void onSettings(); - void onPreview(); - void onUpdateInlineItems(); - void onSwitchPm(); - -signals: - - void selected(DocumentData *sticker); - void selected(PhotoData *photo); - void selected(InlineBots::Result *result, UserData *bot); - - void removing(quint64 setId); - - void refreshIcons(); - void emptyInlineRows(); - - void switchToEmoji(); - - void scrollToY(int y); - void scrollUpdated(); - void disableScroll(bool dis); - void needRefreshPanels(); - - void saveConfigDelayed(int32 delay); - -private: - - void paintInlineItems(Painter &p, const QRect &r); - void paintStickers(Painter &p, const QRect &r); - - void refreshSwitchPmButton(const InlineCacheEntry *entry); - - void appendSet(uint64 setId); - - void selectEmoji(EmojiPtr emoji); - QRect stickerRect(int tab, int sel); - - int32 _maxHeight; - - typedef QMap Animations; // index - showing, -index - hiding - Animations _animations; - Animation _a_selected; - - int32 _top; - - struct DisplayedSet { - DisplayedSet(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) { - } - uint64 id; - MTPDstickerSet::Flags flags; - QString title; - QVector hovers; - StickerPack pack; - }; - QList _sets; - QList _custom; - - bool _showingSavedGifs, _showingInlineItems; - bool _setGifCommand; - UserData *_inlineBot; - QString _inlineBotTitle; - uint64 _lastScrolled; - QTimer _updateInlineItems; - bool _inlineWithThumb; - - std_::unique_ptr _switchPmButton; - QString _switchPmStartToken; - - typedef QVector InlineItems; - struct InlineRow { - InlineRow() : height(0) { - } - int32 height; - InlineItems items; - }; - typedef QVector InlineRows; - InlineRows _inlineRows; - void clearInlineRows(bool resultsDeleted); - - using GifLayouts = QMap; - GifLayouts _gifLayouts; - InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position); - - using InlineLayouts = QMap; - InlineLayouts _inlineLayouts; - InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position); - - bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth); - bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false); - - InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0); - void deleteUnusedGifLayouts(); - - void deleteUnusedInlineLayouts(); - - int32 validateExistingInlineRows(const InlineResults &results); - int32 _selected, _pressedSel; - QPoint _lastMousePos; - - LinkButton _settings; - - QTimer _previewTimer; - bool _previewShown; -}; - -class EmojiPanel : public TWidget { - Q_OBJECT - -public: - - EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY); // Stickers::NoneSetId if in emoji - void setText(const QString &text); - void setDeleteVisible(bool isVisible); - - void paintEvent(QPaintEvent *e); - void mousePressEvent(QMouseEvent *e); - - int32 wantedY() const { - return _wantedY; - } - void setWantedY(int32 y) { - _wantedY = y; - } - -signals: - - void deleteClicked(quint64 setId); - void mousePressed(); - -public slots: - - void onDelete(); - -private: - - void updateText(); - - int32 _wantedY; - QString _text, _fullText; - uint64 _setId; - bool _special, _deleteVisible; - IconedButton *_delete; - -}; - -class EmojiSwitchButton : public Button { -public: - - EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji - void paintEvent(QPaintEvent *e); - void updateText(const QString &inlineBotUsername = QString()); - -protected: - - bool _toStickers; - QString _text; - int32 _textWidth; - -}; - -} // namespace internal - -class EmojiPan : public TWidget, public RPCSender { - Q_OBJECT - -public: - - EmojiPan(QWidget *parent); - - void setMaxHeight(int32 h); - void paintEvent(QPaintEvent *e); - - void moveBottom(int32 bottom, bool force = false); - - void enterEvent(QEvent *e); - void leaveEvent(QEvent *e); - void otherEnter(); - void otherLeave(); - - void mousePressEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - - bool event(QEvent *e); - - void fastHide(); - bool hiding() const { - return _hiding || _hideTimer.isActive(); - } - - void step_appearance(float64 ms, bool timer); - void step_slide(float64 ms, bool timer); - void step_icons(uint64 ms, bool timer); - - bool eventFilter(QObject *obj, QEvent *e); - void stickersInstalled(uint64 setId); - - void queryInlineBot(UserData *bot, PeerData *peer, QString query); - void clearInlineBot(); - - bool overlaps(const QRect &globalRect) { - if (isHidden() || !_cache.isNull()) return false; - - return QRect(st::dropdownDef.padding.left(), - st::dropdownDef.padding.top(), - _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), - _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); - } - - void notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout); - void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout); - bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout); - bool ui_isInlineItemBeingChosen(); - - bool inlineResultsShown() const { - return s_inner.inlineResultsShown(); - } - -public slots: - - void refreshStickers(); - void refreshSavedGifs(); - - void hideStart(); - void hideFinish(); - - void showStart(); - void onWndActiveChanged(); - - void onTabChange(); - void onScrollEmoji(); - void onScrollStickers(); - void onSwitch(); - - void onRemoveSet(quint64 setId); - void onRemoveSetSure(); - void onDelayedHide(); - - void onRefreshIcons(); - void onRefreshPanels(); - - void onSaveConfig(); - void onSaveConfigDelayed(int32 delay); - - void onInlineRequest(); - void onEmptyInlineRows(); - -signals: - - void emojiSelected(EmojiPtr emoji); - void stickerSelected(DocumentData *sticker); - void photoSelected(PhotoData *photo); - void inlineResultSelected(InlineBots::Result *result, UserData *bot); - - void updateStickers(); - -private: - void paintStickerSettingsIcon(Painter &p) const; - - void validateSelectedIcon(bool animated = false); - - int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers; - bool _horizontal; - void updateContentHeight(); - - void leaveToChildEvent(QEvent *e, QWidget *child); - void hideAnimated(); - void prepareShowHideCache(); - - void updateSelected(); - void updateIcons(); - - void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab); - void updatePanelsPositions(const QVector &panels, int32 st); - - void showAll(); - void hideAll(); - - bool _noTabUpdate; - - int32 _width, _height, _bottom; - bool _hiding; - QPixmap _cache; - - anim::fvalue a_opacity; - Animation _a_appearance; - - QTimer _hideTimer; - - BoxShadow _shadow; - - FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols; - QList _icons; - QVector _iconHovers; - int32 _iconOver, _iconSel, _iconDown; - bool _iconsDragging; - typedef QMap Animations; // index - showing, -index - hiding - Animations _iconAnimations; - Animation _a_icons; - QPoint _iconsMousePos, _iconsMouseDown; - int32 _iconsLeft, _iconsTop; - int32 _iconsStartX, _iconsMax; - anim::ivalue _iconsX, _iconSelX; - uint64 _iconsStartAnim; - - bool _stickersShown, _shownFromInlineQuery; - QPixmap _fromCache, _toCache; - anim::ivalue a_fromCoord, a_toCoord; - anim::fvalue a_fromAlpha, a_toAlpha; - Animation _a_slide; - - ScrollArea e_scroll; - internal::EmojiPanInner e_inner; - QVector e_panels; - internal::EmojiSwitchButton e_switch; - ScrollArea s_scroll; - internal::StickerPanInner s_inner; - QVector s_panels; - internal::EmojiSwitchButton s_switch; - - uint64 _removingSetId; - - QTimer _saveConfigTimer; - - // inline bots - typedef QMap InlineCache; - InlineCache _inlineCache; - QTimer _inlineRequestTimer; - - void inlineBotChanged(); - int32 showInlineRows(bool newResults); - bool hideOnNoInlineResults(); - void recountContentMaxHeight(); - bool refreshInlineRows(int32 *added = 0); - UserData *_inlineBot; - PeerData *_inlineQueryPeer = nullptr; - QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; - mtpRequestId _inlineRequestId; - void inlineResultsDone(const MTPmessages_BotResults &result); - bool inlineResultsFail(const RPCError &error); - -}; diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 3150fd647..cbe01c49a 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -193,11 +193,12 @@ enum Flags { namespace Stickers { -static const uint64 DefaultSetId = 0; // for backward compatibility -static const uint64 CustomSetId = 0xFFFFFFFFFFFFFFFFULL; -static const uint64 RecentSetId = 0xFFFFFFFFFFFFFFFEULL; // for emoji/stickers panel, should not appear in Sets -static const uint64 NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, should not appear in Sets -static const uint64 CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers +constexpr uint64 DefaultSetId = 0; // for backward compatibility +constexpr uint64 CustomSetId = 0xFFFFFFFFFFFFFFFFULL; +constexpr uint64 RecentSetId = 0xFFFFFFFFFFFFFFFEULL; // for emoji/stickers panel, should not appear in Sets +constexpr uint64 NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, should not appear in Sets +constexpr uint64 CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers +constexpr uint64 FeaturedSetId = 0xFFFFFFFFFFFFFFFBULL; // for emoji/stickers panel, should not appear in Sets struct Set { Set(uint64 id, uint64 access, const QString &title, const QString &shortName, int32 count, int32 hash, MTPDstickerSet::Flags flags) : id(id) diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index a3a57fb2c..f83fdb289 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -35,8 +35,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "history/history_service_layout.h" #include "profile/profile_members_widget.h" #include "core/click_handler_types.h" +#include "stickers/emoji_pan.h" #include "lang.h" #include "application.h" +#include "dropdown.h" #include "mainwidget.h" #include "mainwindow.h" #include "passcodewidget.h" @@ -3055,11 +3057,11 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_field, SIGNAL(linksChanged()), this, SLOT(onPreviewCheck())); connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onWindowVisibleChanged())); connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); - connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr))); - connect(&_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*))); - connect(&_emojiPan, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*))); - connect(&_emojiPan, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*))); - connect(&_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers())); + connect(_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr))); + connect(_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*))); + connect(_emojiPan, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*))); + connect(_emojiPan, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*))); + connect(_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers())); connect(&_sendActionStopTimer, SIGNAL(timeout()), this, SLOT(onCancelSendAction())); connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreviewTimeout())); if (audioCapture()) { @@ -3130,25 +3132,25 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _silent.hide(); _cmdStart.hide(); - _attachDocument.installEventFilter(&_attachType); - _attachPhoto.installEventFilter(&_attachType); - _attachEmoji.installEventFilter(&_emojiPan); + _attachDocument.installEventFilter(_attachType); + _attachPhoto.installEventFilter(_attachType); + _attachEmoji.installEventFilter(_emojiPan); connect(&_kbShow, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(&_kbHide, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(&_cmdStart, SIGNAL(clicked()), this, SLOT(onCmdStart())); - connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect())); - connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect())); - _attachType.hide(); - _emojiPan.hide(); - _attachDragDocument.hide(); - _attachDragPhoto.hide(); + connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect())); + connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect())); + _attachType->hide(); + _emojiPan->hide(); + _attachDragDocument->hide(); + _attachDragPhoto->hide(); _topShadow.hide(); - connect(&_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*))); - connect(&_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*))); + connect(_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*))); + connect(_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*))); connect(&_updateEditTimeLeftDisplay, SIGNAL(timeout()), this, SLOT(updateField())); @@ -3157,7 +3159,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) void HistoryWidget::start() { connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); - connect(App::main(), SIGNAL(savedGifsUpdated()), &_emojiPan, SLOT(refreshSavedGifs())); + connect(App::main(), SIGNAL(savedGifsUpdated()), _emojiPan, SLOT(refreshSavedGifs())); updateRecentStickers(); if (App::main()) emit App::main()->savedGifsUpdated(); @@ -3166,7 +3168,7 @@ void HistoryWidget::start() { } void HistoryWidget::onStickersUpdated() { - _emojiPan.refreshStickers(); + _emojiPan->refreshStickers(); updateStickersByEmoji(); } @@ -3228,9 +3230,9 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { inlineBotChanged(); } if (_inlineBot->username == cInlineGifBotUsername() && query.isEmpty()) { - _emojiPan.clearInlineBot(); + _emojiPan->clearInlineBot(); } else { - _emojiPan.queryInlineBot(_inlineBot, _peer, query); + _emojiPan->queryInlineBot(_inlineBot, _peer, query); } if (!_fieldAutocomplete->isHidden()) { _fieldAutocomplete->hideStart(); @@ -3450,11 +3452,11 @@ void HistoryWidget::updateSendAction(History *history, SendActionType type, int3 } void HistoryWidget::updateRecentStickers() { - _emojiPan.refreshStickers(); + _emojiPan->refreshStickers(); } void HistoryWidget::stickersInstalled(uint64 setId) { - _emojiPan.stickersInstalled(setId); + _emojiPan->stickersInstalled(setId); } void HistoryWidget::sendActionDone(const MTPBool &result, mtpRequestId req) { @@ -4380,11 +4382,11 @@ void HistoryWidget::updateNotifySettings() { } bool HistoryWidget::contentOverlapped(const QRect &globalRect) { - return (_attachDragDocument.overlaps(globalRect) || - _attachDragPhoto.overlaps(globalRect) || - _attachType.overlaps(globalRect) || + return (_attachDragDocument->overlaps(globalRect) || + _attachDragPhoto->overlaps(globalRect) || + _attachType->overlaps(globalRect) || _fieldAutocomplete->overlaps(globalRect) || - _emojiPan.overlaps(globalRect)); + _emojiPan->overlaps(globalRect)); } void HistoryWidget::updateReportSpamStatus() { @@ -4516,8 +4518,8 @@ void HistoryWidget::updateControlsVisibility() { _kbShow.hide(); _kbHide.hide(); _cmdStart.hide(); - _attachType.hide(); - _emojiPan.hide(); + _attachType->hide(); + _emojiPan->hide(); if (_pinnedBar) { _pinnedBar->cancel.hide(); _pinnedBar->shadow.hide(); @@ -4579,8 +4581,8 @@ void HistoryWidget::updateControlsVisibility() { _kbShow.hide(); _kbHide.hide(); _cmdStart.hide(); - _attachType.hide(); - _emojiPan.hide(); + _attachType->hide(); + _emojiPan->hide(); if (!_field.isHidden()) { _field.hide(); resizeEvent(0); @@ -4715,8 +4717,8 @@ void HistoryWidget::updateControlsVisibility() { _kbShow.hide(); _kbHide.hide(); _cmdStart.hide(); - _attachType.hide(); - _emojiPan.hide(); + _attachType->hide(); + _emojiPan->hide(); _kbScroll.hide(); if (!_field.isHidden()) { _field.hide(); @@ -5257,8 +5259,8 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { onDraftSave(); if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); if (replyTo < 0) cancelReply(lastKeyboardUsed); if (_previewData && _previewData->pendingTill) previewCancel(); @@ -5586,7 +5588,7 @@ void HistoryWidget::onPhotoSelect() { _attachDocument.clearState(); _attachDocument.hide(); _attachPhoto.show(); - _attachType.fastHide(); + _attachType->fastHide(); if (cDefaultAttach() != dbidaPhoto) { cSetDefaultAttach(dbidaPhoto); @@ -5614,7 +5616,7 @@ void HistoryWidget::onDocumentSelect() { _attachPhoto.clearState(); _attachPhoto.hide(); _attachDocument.show(); - _attachType.fastHide(); + _attachType->fastHide(); if (cDefaultAttach() != dbidaDocument) { cSetDefaultAttach(dbidaDocument); @@ -5651,14 +5653,14 @@ void HistoryWidget::dragEnterEvent(QDragEnterEvent *e) { } void HistoryWidget::dragLeaveEvent(QDragLeaveEvent *e) { - if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) { + if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; updateDragAreas(); } } void HistoryWidget::leaveEvent(QEvent *e) { - if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) { + if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; updateDragAreas(); } @@ -5706,7 +5708,7 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) { _replyForwardPressed = false; update(0, _field.y() - st::sendPadding - st::replyHeight, width(), st::replyHeight); } - if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) { + if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; updateDragAreas(); } @@ -5961,24 +5963,24 @@ void HistoryWidget::updateDragAreas() { _field.setAcceptDrops(!_attachDrag); switch (_attachDrag) { case DragStateNone: - _attachDragDocument.otherLeave(); - _attachDragPhoto.otherLeave(); + _attachDragDocument->otherLeave(); + _attachDragPhoto->otherLeave(); break; case DragStateFiles: - _attachDragDocument.otherEnter(); - _attachDragDocument.setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files)); - _attachDragPhoto.fastHide(); + _attachDragDocument->otherEnter(); + _attachDragDocument->setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files)); + _attachDragPhoto->fastHide(); break; case DragStatePhotoFiles: - _attachDragDocument.otherEnter(); - _attachDragDocument.setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression)); - _attachDragPhoto.otherEnter(); - _attachDragPhoto.setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick)); + _attachDragDocument->otherEnter(); + _attachDragDocument->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression)); + _attachDragPhoto->otherEnter(); + _attachDragPhoto->setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick)); break; case DragStateImage: - _attachDragDocument.fastHide(); - _attachDragPhoto.otherEnter(); - _attachDragPhoto.setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick)); + _attachDragDocument->fastHide(); + _attachDragPhoto->otherEnter(); + _attachDragPhoto->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick)); break; }; resizeEvent(0); @@ -6460,8 +6462,8 @@ void HistoryWidget::moveFieldControls() { right = w; _fieldBarCancel.move(right - _fieldBarCancel.width(), _field.y() - st::sendPadding - _fieldBarCancel.height()); - _attachType.move(0, _attachDocument.y() - _attachType.height()); - _emojiPan.moveBottom(_attachEmoji.y()); + _attachType->move(0, _attachDocument.y() - _attachType->height()); + _emojiPan->moveBottom(_attachEmoji.y()); _botStart.setGeometry(0, bottom - _botStart.height(), w, _botStart.height()); _unblock.setGeometry(0, bottom - _unblock.height(), w, _unblock.height()); @@ -6491,7 +6493,7 @@ void HistoryWidget::clearInlineBot() { inlineBotChanged(); _field.finishPlaceholder(); } - _emojiPan.clearInlineBot(); + _emojiPan->clearInlineBot(); onCheckFieldAutocomplete(); } @@ -6958,15 +6960,15 @@ void HistoryWidget::onUpdateHistoryItems() { } void HistoryWidget::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { - _emojiPan.ui_repaintInlineItem(layout); + _emojiPan->ui_repaintInlineItem(layout); } bool HistoryWidget::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { - return _emojiPan.ui_isInlineItemVisible(layout); + return _emojiPan->ui_isInlineItemVisible(layout); } bool HistoryWidget::ui_isInlineItemBeingChosen() { - return _emojiPan.ui_isInlineItemBeingChosen(); + return _emojiPan->ui_isInlineItemBeingChosen(); } PeerData *HistoryWidget::ui_getPeerForMouseAction() { @@ -6980,7 +6982,7 @@ void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { } void HistoryWidget::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { - _emojiPan.notify_inlineItemLayoutChanged(layout); + _emojiPan->notify_inlineItemLayoutChanged(layout); } void HistoryWidget::notify_handlePendingHistoryUpdate() { @@ -7015,25 +7017,25 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll.y() + _scroll.height() - _historyToEnd->height() - st::historyToDownPosition.y()); - _emojiPan.setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); + _emojiPan->setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); if (_membersDropdown) { _membersDropdown->setMaxHeight(countMembersDropdownHeightMax()); } switch (_attachDrag) { case DragStateFiles: - _attachDragDocument.resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); - _attachDragDocument.move(st::dragMargin.left(), st::dragMargin.top()); + _attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); + _attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top()); break; case DragStatePhotoFiles: - _attachDragDocument.resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2); - _attachDragDocument.move(st::dragMargin.left(), st::dragMargin.top()); - _attachDragPhoto.resize(_attachDragDocument.width(), _attachDragDocument.height()); - _attachDragPhoto.move(st::dragMargin.left(), height() - _attachDragPhoto.height() - st::dragMargin.bottom()); + _attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2); + _attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top()); + _attachDragPhoto->resize(_attachDragDocument->width(), _attachDragDocument->height()); + _attachDragPhoto->move(st::dragMargin.left(), height() - _attachDragPhoto->height() - st::dragMargin.bottom()); break; case DragStateImage: - _attachDragPhoto.resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); - _attachDragPhoto.move(st::dragMargin.left(), st::dragMargin.top()); + _attachDragPhoto->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); + _attachDragPhoto->move(st::dragMargin.left(), st::dragMargin.top()); break; } @@ -7530,8 +7532,8 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot } if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); _field.setFocus(); } @@ -7600,10 +7602,10 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { if (_membersDropdown) { _membersDropdown->raise(); } - _attachType.raise(); - _emojiPan.raise(); - _attachDragDocument.raise(); - _attachDragPhoto.raise(); + _attachType->raise(); + _emojiPan->raise(); + _attachDragDocument->raise(); + _attachDragPhoto->raise(); updatePinnedBar(); result = true; @@ -7701,8 +7703,8 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti } if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); _field.setFocus(); } @@ -7747,8 +7749,8 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) App::historyRegRandom(randomId, newId); if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType.isHidden()) _attachType.hideStart(); - if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + if (!_attachType->isHidden()) _attachType->hideStart(); + if (!_emojiPan->isHidden()) _emojiPan->hideStart(); _field.setFocus(); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index e73e2b87d..7d4133487 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localimageloader.h" #include "ui/boxshadow.h" -#include "dropdown.h" #include "history/history_common.h" #include "history/field_autocomplete.h" #include "window/section_widget.h" @@ -39,6 +38,10 @@ class HistoryDownButton; class InnerDropdown; } // namespace Ui +class Dropdown; +class DragArea; +class EmojiPan; + class HistoryWidget; class HistoryInner : public TWidget, public AbstractTooltipShower { Q_OBJECT @@ -1124,10 +1127,10 @@ private: ChildWidget _membersDropdown = { nullptr }; QTimer _membersDropdownShowTimer; - Dropdown _attachType; - EmojiPan _emojiPan; + ChildWidget _attachType; + ChildWidget _emojiPan; DragState _attachDrag = DragStateNone; - DragArea _attachDragDocument, _attachDragPhoto; + ChildWidget _attachDragDocument, _attachDragPhoto; int32 _selCount; // < 0 - text selected, focus list, not _field diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index af1ca0e98..8feba33bc 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/section_widget.h" #include "window/top_bar_widget.h" #include "data/data_drafts.h" +#include "dropdown.h" #include "observer_peer.h" #include "apiwrap.h" #include "dialogswidget.h" diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp new file mode 100644 index 000000000..f84cc454b --- /dev/null +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -0,0 +1,3704 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "stickers/emoji_pan.h" + +#include "styles/style_stickers.h" +#include "boxes/confirmbox.h" +#include "boxes/stickersetbox.h" +#include "inline_bots/inline_bot_result.h" +#include "inline_bots/inline_bot_layout_item.h" +#include "dialogs/dialogs_layout.h" +#include "stickers/stickers.h" +#include "historywidget.h" +#include "localstorage.h" +#include "lang.h" +#include "mainwindow.h" +#include "apiwrap.h" +#include "mainwidget.h" + +namespace internal { + +EmojiColorPicker::EmojiColorPicker() : TWidget() +, _a_selected(animation(this, &EmojiColorPicker::step_selected)) +, a_opacity(0) +, _a_appearance(animation(this, &EmojiColorPicker::step_appearance)) +, _shadow(st::dropdownDef.shadow) { + memset(_variants, 0, sizeof(_variants)); + memset(_hovers, 0, sizeof(_hovers)); + + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + + int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::dropdownDef.shadow.pxWidth() * 2; + int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::dropdownDef.shadow.pxHeight() * 2; + resize(w, h); + + _hideTimer.setSingleShot(true); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); +} + +void EmojiColorPicker::showEmoji(uint32 code) { + EmojiPtr e = emojiGet(code); + if (!e || e == TwoSymbolEmoji || !e->color) { + return; + } + _ignoreShow = false; + + _variants[0] = e; + _variants[1] = emojiGet(e, 0xD83CDFFB); + _variants[2] = emojiGet(e, 0xD83CDFFC); + _variants[3] = emojiGet(e, 0xD83CDFFD); + _variants[4] = emojiGet(e, 0xD83CDFFE); + _variants[5] = emojiGet(e, 0xD83CDFFF); + + if (!_cache.isNull()) _cache = QPixmap(); + showStart(); +} + +void EmojiColorPicker::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_cache.isNull()) { + p.setOpacity(a_opacity.current()); + } + if (e->rect() != rect()) { + p.setClipRect(e->rect()); + } + + int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); + QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h); + _shadow.paint(p, r, st::dropdownDef.shadowShift); + + if (_cache.isNull()) { + p.fillRect(e->rect().intersected(r), st::white->b); + + int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width(); + if (rtl()) x = width() - x - st::emojiColorsSep; + p.fillRect(x, h + st::emojiColorsPadding, st::emojiColorsSep, r.height() - st::emojiColorsPadding * 2, st::emojiColorsSepColor->b); + + if (!_variants[0]) return; + for (int i = 0; i < EmojiColorsCount + 1; ++i) { + drawVariant(p, i); + } + } else { + p.drawPixmap(r.left(), r.top(), _cache); + } + +} + +void EmojiColorPicker::enterEvent(QEvent *e) { + _hideTimer.stop(); + if (_hiding) showStart(); + TWidget::enterEvent(e); +} + +void EmojiColorPicker::leaveEvent(QEvent *e) { + TWidget::leaveEvent(e); +} + +void EmojiColorPicker::mousePressEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); + _pressedSel = _selected; +} + +void EmojiColorPicker::mouseReleaseEvent(QMouseEvent *e) { + _lastMousePos = e ? e->globalPos() : QCursor::pos(); + int32 pressed = _pressedSel; + _pressedSel = -1; + + updateSelected(); + if (_selected >= 0 && (pressed < 0 || _selected == pressed)) { + emit emojiSelected(_variants[_selected]); + } + _ignoreShow = true; + hideStart(); +} + +void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) { + _lastMousePos = e ? e->globalPos() : QCursor::pos(); + updateSelected(); +} + +void EmojiColorPicker::step_appearance(float64 ms, bool timer) { + if (_cache.isNull()) { + _a_appearance.stop(); + return; + } + float64 dt = ms / st::dropdownDef.duration; + if (dt >= 1) { + a_opacity.finish(); + _cache = QPixmap(); + if (_hiding) { + hide(); + emit hidden(); + } else { + _lastMousePos = QCursor::pos(); + updateSelected(); + } + _a_appearance.stop(); + } else { + a_opacity.update(dt, anim::linear); + } + if (timer) update(); +} + +void EmojiColorPicker::step_selected(uint64 ms, bool timer) { + QRegion toUpdate; + for (EmojiAnimations::iterator i = _emojiAnimations.begin(); i != _emojiAnimations.end();) { + int index = qAbs(i.key()) - 1; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (dt >= 1) { + _hovers[index] = (i.key() > 0) ? 1 : 0; + i = _emojiAnimations.erase(i); + } else { + _hovers[index] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + toUpdate += QRect(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height()); + } + if (timer) rtlupdate(toUpdate.boundingRect()); + if (_emojiAnimations.isEmpty()) _a_selected.stop(); +} + +void EmojiColorPicker::hideStart(bool fast) { + if (fast) { + clearSelection(true); + if (_a_appearance.animating()) _a_appearance.stop(); + if (_a_selected.animating()) _a_selected.stop(); + a_opacity = anim::fvalue(0); + _cache = QPixmap(); + hide(); + emit hidden(); + } else { + if (_cache.isNull()) { + int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); + _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); + clearSelection(true); + } + _hiding = true; + a_opacity.start(0); + _a_appearance.start(); + } +} + +void EmojiColorPicker::showStart() { + if (_ignoreShow) return; + + _hiding = false; + if (!isHidden() && a_opacity.current() == 1) { + if (_a_appearance.animating()) { + _a_appearance.stop(); + _cache = QPixmap(); + } + return; + } + if (_cache.isNull()) { + int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight(); + _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); + clearSelection(true); + } + show(); + a_opacity.start(1); + _a_appearance.start(); +} + +void EmojiColorPicker::clearSelection(bool fast) { + _pressedSel = -1; + _lastMousePos = mapToGlobal(QPoint(-10, -10)); + if (fast) { + _selected = -1; + memset(_hovers, 0, sizeof(_hovers)); + _emojiAnimations.clear(); + } else { + updateSelected(); + } +} + +void EmojiColorPicker::updateSelected() { + int32 selIndex = -1; + QPoint p(mapFromGlobal(_lastMousePos)); + int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::dropdownDef.shadow.pxHeight() - st::emojiColorsPadding; + if (y >= 0 && y < st::emojiPanSize.height()) { + int32 x = sx - st::dropdownDef.shadow.pxWidth() - st::emojiColorsPadding; + if (x >= 0 && x < st::emojiPanSize.width()) { + selIndex = 0; + } else { + x -= st::emojiPanSize.width() + 2 * st::emojiColorsPadding + st::emojiColorsSep; + if (x >= 0 && x < st::emojiPanSize.width() * EmojiColorsCount) { + selIndex = (x / st::emojiPanSize.width()) + 1; + } + } + } + + bool startanim = false; + if (selIndex != _selected) { + if (_selected >= 0) { + _emojiAnimations.remove(_selected + 1); + if (_emojiAnimations.find(-_selected - 1) == _emojiAnimations.end()) { + if (_emojiAnimations.isEmpty()) startanim = true; + _emojiAnimations.insert(-_selected - 1, getms()); + } + } + _selected = selIndex; + if (_selected >= 0) { + _emojiAnimations.remove(-_selected - 1); + if (_emojiAnimations.find(_selected + 1) == _emojiAnimations.end()) { + if (_emojiAnimations.isEmpty()) startanim = true; + _emojiAnimations.insert(_selected + 1, getms()); + } + } + setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default); + } + if (startanim && !_a_selected.animating()) _a_selected.start(); +} + +void EmojiColorPicker::drawVariant(Painter &p, int variant) { + float64 hover = _hovers[variant]; + + QPoint w(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding); + if (hover > 0) { + p.setOpacity(hover); + QPoint tl(w); + if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); + App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); + p.setOpacity(1); + } + int esize = EmojiSizes[EIndex + 1]; + p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_variants[variant]->x * esize, _variants[variant]->y * esize, esize, esize)); +} + +EmojiPanInner::EmojiPanInner() : TWidget() +, _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height) +, _a_selected(animation(this, &EmojiPanInner::step_selected)) { + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); + + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setAttribute(Qt::WA_OpaquePaintEvent); + + _picker.hide(); + + _esize = EmojiSizes[EIndex + 1]; + + for (int32 i = 0; i < emojiTabCount; ++i) { + _counts[i] = emojiPackCount(emojiTabAtIndex(i)); + _hovers[i] = QVector(_counts[i], 0); + } + + _showPickerTimer.setSingleShot(true); + connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker())); + connect(&_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr))); + connect(&_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden())); +} + +void EmojiPanInner::setMaxHeight(int32 h) { + _maxHeight = h; + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); +} + +void EmojiPanInner::setScrollTop(int top) { + _top = top; +} + +int EmojiPanInner::countHeight() { + int result = 0; + for (int i = 0; i < emojiTabCount; ++i) { + int cnt = emojiPackCount(emojiTabAtIndex(i)), rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + result += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + + return result + st::emojiPanPadding; +} + +void EmojiPanInner::paintEvent(QPaintEvent *e) { + Painter p(this); + QRect r = e ? e->rect() : rect(); + if (r != rect()) { + p.setClipRect(r); + } + p.fillRect(r, st::white->b); + + int32 fromcol = floorclamp(r.x() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - st::emojiPanPadding, st::emojiPanSize.width(), 0, EmojiPanPerRow); + if (rtl()) { + qSwap(fromcol, tocol); + fromcol = EmojiPanPerRow - fromcol; + tocol = EmojiPanPerRow - tocol; + } + + int32 y, tilly = 0; + for (int c = 0; c < emojiTabCount; ++c) { + y = tilly; + int32 size = _counts[c]; + int32 rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); + tilly = y + st::emojiPanHeader + (rows * st::emojiPanSize.height()); + if (r.top() >= tilly) continue; + + y += st::emojiPanHeader; + if (_emojis[c].isEmpty()) { + _emojis[c] = emojiPack(emojiTabAtIndex(c)); + if (emojiTabAtIndex(c) != dbietRecent) { + for (EmojiPack::iterator i = _emojis[c].begin(), e = _emojis[c].end(); i != e; ++i) { + if ((*i)->color) { + EmojiColorVariants::const_iterator j = cEmojiVariants().constFind((*i)->code); + if (j != cEmojiVariants().cend()) { + EmojiPtr replace = emojiFromKey(j.value()); + if (replace) { + if (replace != TwoSymbolEmoji && replace->code == (*i)->code && replace->code2 == (*i)->code2) { + *i = replace; + } + } + } + } + } + } + } + + int32 fromrow = floorclamp(r.y() - y, st::emojiPanSize.height(), 0, rows); + int32 torow = ceilclamp(r.y() + r.height() - y, st::emojiPanSize.height(), 0, rows); + for (int32 i = fromrow; i < torow; ++i) { + for (int32 j = fromcol; j < tocol; ++j) { + int32 index = i * EmojiPanPerRow + j; + if (index >= size) break; + + float64 hover = (!_picker.isHidden() && c * MatrixRowShift + index == _pickerSel) ? 1 : _hovers[c][index]; + + QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height()); + if (hover > 0) { + p.setOpacity(hover); + QPoint tl(w); + if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); + App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); + p.setOpacity(1); + } + p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (_esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (_esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_emojis[c][index]->x * _esize, _emojis[c][index]->y * _esize, _esize, _esize)); + } + } + } +} + +bool EmojiPanInner::checkPickerHide() { + if (!_picker.isHidden() && _selected == _pickerSel) { + _picker.hideStart(); + _pickerSel = -1; + updateSelected(); + return true; + } + return false; +} + +void EmojiPanInner::mousePressEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); + if (checkPickerHide()) { + return; + } + _pressedSel = _selected; + + if (_selected >= 0) { + int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; + if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { + _pickerSel = _selected; + setCursor(style::cur_default); + if (cEmojiVariants().constFind(_emojis[tab][sel]->code) == cEmojiVariants().cend()) { + onShowPicker(); + } else { + _showPickerTimer.start(500); + } + } + } +} + +void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { + int32 pressed = _pressedSel; + _pressedSel = -1; + + _lastMousePos = e->globalPos(); + if (!_picker.isHidden()) { + if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { + return _picker.mouseReleaseEvent(0); + } else if (_pickerSel >= 0) { + int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; + if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { + if (cEmojiVariants().constFind(_emojis[tab][sel]->code) != cEmojiVariants().cend()) { + _picker.hideStart(); + _pickerSel = -1; + } + } + } + } + updateSelected(); + + if (_showPickerTimer.isActive()) { + _showPickerTimer.stop(); + _pickerSel = -1; + _picker.hide(); + } + + if (_selected < 0 || _selected != pressed) return; + + if (_selected >= emojiTabCount * MatrixRowShift) { + return; + } + + int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; + if (sel < _emojis[tab].size()) { + EmojiPtr emoji(_emojis[tab][sel]); + if (emoji->color && !_picker.isHidden()) return; + + selectEmoji(emoji); + } +} + +void EmojiPanInner::selectEmoji(EmojiPtr emoji) { + RecentEmojiPack &recent(cGetRecentEmojis()); + RecentEmojiPack::iterator i = recent.begin(), e = recent.end(); + for (; i != e; ++i) { + if (i->first == emoji) { + ++i->second; + if (i->second > 0x8000) { + for (RecentEmojiPack::iterator j = recent.begin(); j != e; ++j) { + if (j->second > 1) { + j->second /= 2; + } else { + j->second = 1; + } + } + } + for (; i != recent.begin(); --i) { + if ((i - 1)->second > i->second) { + break; + } + qSwap(*i, *(i - 1)); + } + break; + } + } + if (i == e) { + while (recent.size() >= EmojiPanPerRow * EmojiPanRowsPerPage) recent.pop_back(); + recent.push_back(qMakePair(emoji, 1)); + for (i = recent.end() - 1; i != recent.begin(); --i) { + if ((i - 1)->second > i->second) { + break; + } + qSwap(*i, *(i - 1)); + } + } + emit saveConfigDelayed(SaveRecentEmojisTimeout); + + emit selected(emoji); +} + +void EmojiPanInner::onShowPicker() { + if (_pickerSel < 0) return; + + int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; + if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { + int32 y = 0; + for (int c = 0; c <= tab; ++c) { + int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + (rows * st::emojiPanSize.height()); + } + y -= _picker.height() - st::buttonRadius + _top; + if (y < 0) { + y += _picker.height() - st::buttonRadius + st::emojiPanSize.height() - st::buttonRadius; + } + int xmax = width() - _picker.width(); + float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1); + if (rtl()) coef = 1. - coef; + _picker.move(qRound(xmax * coef), y); + + _picker.showEmoji(_emojis[tab][sel]->code); + emit disableScroll(true); + } +} + +void EmojiPanInner::onPickerHidden() { + _pickerSel = -1; + update(); + emit disableScroll(false); + + _lastMousePos = QCursor::pos(); + updateSelected(); +} + +QRect EmojiPanInner::emojiRect(int tab, int sel) { + int x = 0, y = 0; + for (int i = 0; i < emojiTabCount; ++i) { + if (i == tab) { + int rows = (sel / EmojiPanPerRow); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + x = st::emojiPanPadding + ((sel % EmojiPanPerRow) * st::emojiPanSize.width()); + break; + } else { + int cnt = _counts[i]; + int rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + } + return QRect(x, y, st::emojiPanSize.width(), st::emojiPanSize.height()); +} + +void EmojiPanInner::onColorSelected(EmojiPtr emoji) { + if (emoji->color) { + cRefEmojiVariants().insert(emoji->code, emojiKey(emoji)); + } + if (_pickerSel >= 0) { + int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; + if (tab >= 0 && tab < emojiTabCount) { + _emojis[tab][sel] = emoji; + rtlupdate(emojiRect(tab, sel)); + } + } + selectEmoji(emoji); + _picker.hideStart(); +} + +void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + if (!_picker.isHidden()) { + if (_picker.rect().contains(_picker.mapFromGlobal(_lastMousePos))) { + return _picker.mouseMoveEvent(0); + } else { + _picker.clearSelection(); + } + } + updateSelected(); +} + +void EmojiPanInner::leaveEvent(QEvent *e) { + clearSelection(); +} + +void EmojiPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { + clearSelection(); +} + +void EmojiPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { + _lastMousePos = QCursor::pos(); + updateSelected(); +} + +void EmojiPanInner::clearSelection(bool fast) { + _lastMousePos = mapToGlobal(QPoint(-10, -10)); + if (fast) { + for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } + _animations.clear(); + if (_selected >= 0) { + int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } + if (_pressedSel >= 0) { + int index = qAbs(_pressedSel), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + _hovers[tab][sel] = 0; + } + _selected = _pressedSel = -1; + _a_selected.stop(); + } else { + updateSelected(); + } +} + +DBIEmojiTab EmojiPanInner::currentTab(int yOffset) const { + int y, ytill = 0; + for (int c = 0; c < emojiTabCount; ++c) { + int cnt = _counts[c]; + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); + if (yOffset < ytill) { + return emojiTabAtIndex(c); + } + } + return emojiTabAtIndex(emojiTabCount - 1); +} + +void EmojiPanInner::hideFinish() { + if (!_picker.isHidden()) { + _picker.hideStart(true); + _pickerSel = -1; + clearSelection(true); + } +} + +void EmojiPanInner::refreshRecent() { + clearSelection(true); + _counts[0] = emojiPackCount(dbietRecent); + if (_hovers[0].size() != _counts[0]) _hovers[0] = QVector(_counts[0], 0); + _emojis[0] = emojiPack(dbietRecent); + int32 h = countHeight(); + if (h != height()) { + resize(width(), h); + emit needRefreshPanels(); + } +} + +void EmojiPanInner::fillPanels(QVector &panels) { + if (_picker.parentWidget() != parentWidget()) { + _picker.setParent(parentWidget()); + } + for (int32 i = 0; i < panels.size(); ++i) { + panels.at(i)->hide(); + panels.at(i)->deleteLater(); + } + panels.clear(); + + int y = 0; + panels.reserve(emojiTabCount); + for (int c = 0; c < emojiTabCount; ++c) { + panels.push_back(new EmojiPanel(parentWidget(), lang(LangKey(lng_emoji_category0 + c)), Stickers::NoneSetId, true, y)); + connect(panels.back(), SIGNAL(mousePressed()), this, SLOT(checkPickerHide())); + int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + panels.back()->show(); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + _picker.raise(); +} + +void EmojiPanInner::refreshPanels(QVector &panels) { + if (panels.size() != emojiTabCount) return fillPanels(panels); + + int32 y = 0; + for (int c = 0; c < emojiTabCount; ++c) { + panels.at(c)->setWantedY(y); + int cnt = _counts[c], rows = (cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } +} + +void EmojiPanInner::updateSelected() { + if (_pressedSel >= 0 || _pickerSel >= 0) return; + + int32 selIndex = -1; + QPoint p(mapFromGlobal(_lastMousePos)); + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::emojiPanPadding; + for (int c = 0; c < emojiTabCount; ++c) { + int cnt = _counts[c]; + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / EmojiPanPerRow) + ((cnt % EmojiPanPerRow) ? 1 : 0)) * st::emojiPanSize.height(); + if (p.y() >= y && p.y() < ytill) { + y += st::emojiPanHeader; + if (p.y() >= y && sx >= 0 && sx < EmojiPanPerRow * st::emojiPanSize.width()) { + selIndex = qFloor((p.y() - y) / st::emojiPanSize.height()) * EmojiPanPerRow + qFloor(sx / st::emojiPanSize.width()); + if (selIndex >= _emojis[c].size()) { + selIndex = -1; + } else { + selIndex += c * MatrixRowShift; + } + } + break; + } + } + + bool startanim = false; + int oldSel = _selected, newSel = selIndex; + + if (newSel != oldSel) { + if (oldSel >= 0) { + _animations.remove(oldSel + 1); + if (_animations.find(-oldSel - 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(-oldSel - 1, getms()); + } + } + if (newSel >= 0) { + _animations.remove(-newSel - 1); + if (_animations.find(newSel + 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(newSel + 1, getms()); + } + } + setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default); + if (newSel >= 0 && !_picker.isHidden()) { + if (newSel != _pickerSel) { + _picker.hideStart(); + } else { + _picker.showStart(); + } + } + } + + _selected = selIndex; + if (startanim && !_a_selected.animating()) _a_selected.start(); +} + +void EmojiPanInner::step_selected(uint64 ms, bool timer) { + QRegion toUpdate; + for (Animations::iterator i = _animations.begin(); i != _animations.end();) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (dt >= 1) { + _hovers[tab][sel] = (i.key() > 0) ? 1 : 0; + i = _animations.erase(i); + } else { + _hovers[tab][sel] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + toUpdate += emojiRect(tab, sel); + } + if (timer) rtlupdate(toUpdate.boundingRect()); + if (_animations.isEmpty()) _a_selected.stop(); +} + +void InlineCacheEntry::clearResults() { + for_const (const InlineBots::Result *result, results) { + delete result; + } + results.clear(); +} + +void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { + clearSelection(true); + + refreshRecent(); + + int32 y = 0; + for (int c = 0; c < emojiTabCount; ++c) { + if (emojiTabAtIndex(c) == packIndex) break; + int rows = (_counts[c] / EmojiPanPerRow) + ((_counts[c] % EmojiPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::emojiPanSize.height(); + } + + emit scrollToY(y); + + _lastMousePos = QCursor::pos(); + + update(); +} + +StickerPanInner::StickerPanInner() : TWidget() +, _a_selected(animation(this, &StickerPanInner::step_selected)) +, _section(cShowingSavedGifs() ? Section::Gifs : Section::Stickers) +, _addText(lang(lng_stickers_featured_add).toUpper()) +, _addWidth(st::featuredStickersAdd.font->width(_addText)) +, _settings(this, lang(lng_stickers_you_have)) { + setMaxHeight(st::emojiPanMaxHeight - st::rbEmoji.height); + + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setAttribute(Qt::WA_OpaquePaintEvent); + + connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); + connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); + + _previewTimer.setSingleShot(true); + connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); + + _updateInlineItems.setSingleShot(true); + connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems())); +} + +void StickerPanInner::setMaxHeight(int32 h) { + _maxHeight = h; + resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); + _settings.moveToLeft((st::emojiPanWidth - _settings.width()) / 2, height() / 3); +} + +void StickerPanInner::setScrollTop(int top) { + if (top == _top) return; + + _lastScrolled = getms(); + _top = top; +} + +int StickerPanInner::featuredRowHeight() const { + return st::featuredStickersHeader + st::stickerPanSize.height() + st::featuredStickersSkip; +} + +int StickerPanInner::countHeight(bool plain) { + int result = 0, minLastH = plain ? 0 : (_maxHeight - st::stickerPanPadding); + if (showingInlineItems()) { + result = st::emojiPanHeader; + if (_switchPmButton) { + result += _switchPmButton->height() + st::inlineResultsSkip; + } + for (int i = 0, l = _inlineRows.count(); i < l; ++i) { + result += _inlineRows[i].height; + } + } else if (_section == Section::Featured) { + result = st::emojiPanHeader + shownSets().size() * featuredRowHeight(); + } else { + auto &sets = shownSets(); + for (int i = 0; i < sets.size(); ++i) { + int cnt = sets[i].pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + if (i == sets.size() - 1 && h < minLastH) h = minLastH; + result += h; + } + } + return qMax(minLastH, result) + st::stickerPanPadding; +} + +StickerPanInner::~StickerPanInner() { + clearInlineRows(true); + deleteUnusedGifLayouts(); + deleteUnusedInlineLayouts(); +} + +QRect StickerPanInner::stickerRect(int tab, int sel) { + int x = 0, y = 0; + if (_section == Section::Featured) { + y += st::emojiPanHeader + (tab * featuredRowHeight()) + st::featuredStickersHeader; + x = st::stickerPanPadding + (sel * st::stickerPanSize.width()); + } else { + auto &sets = shownSets(); + for (int i = 0; i < sets.size(); ++i) { + if (i == tab) { + int rows = (((sel >= sets[i].pack.size()) ? (sel - sets[i].pack.size()) : sel) / StickerPanPerRow); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + x = st::stickerPanPadding + ((sel % StickerPanPerRow) * st::stickerPanSize.width()); + break; + } else { + int cnt = sets[i].pack.size(); + int rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + } + } + } + return QRect(x, y, st::stickerPanSize.width(), st::stickerPanSize.height()); +} + +void StickerPanInner::paintEvent(QPaintEvent *e) { + Painter p(this); + QRect r = e ? e->rect() : rect(); + if (r != rect()) { + p.setClipRect(r); + } + p.fillRect(r, st::white); + + if (showingInlineItems()) { + paintInlineItems(p, r); + } else { + paintStickers(p, r); + } +} + +void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) { + if (_inlineRows.isEmpty()) { + p.setFont(st::normalFont); + p.setPen(st::noContactsColor); + p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), lang(lng_inline_bot_no_results), style::al_center); + return; + } + InlineBots::Layout::PaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false); + + int top = st::emojiPanHeader; + if (_switchPmButton) { + top += _switchPmButton->height() + st::inlineResultsSkip; + } + + int fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width()); + for (int row = 0, rows = _inlineRows.size(); row < rows; ++row) { + auto &inlineRow = _inlineRows[row]; + if (top >= r.top() + r.height()) break; + if (top + inlineRow.height > r.top()) { + int left = st::inlineResultsLeft; + if (row == rows - 1) context.lastRow = true; + for (int col = 0, cols = inlineRow.items.size(); col < cols; ++col) { + if (left >= tox) break; + + const InlineItem *item = inlineRow.items.at(col); + int w = item->width(); + if (left + w > fromx) { + p.translate(left, top); + item->paint(p, r.translated(-left, -top), &context); + p.translate(-left, -top); + } + left += w; + if (item->hasRightSkip()) { + left += st::inlineResultsSkip; + } + } + } + top += inlineRow.height; + } +} + +void StickerPanInner::paintStickers(Painter &p, const QRect &r) { + int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); + if (rtl()) { + qSwap(fromcol, tocol); + fromcol = StickerPanPerRow - fromcol; + tocol = StickerPanPerRow - tocol; + } + + int y, tilly = 0; + + auto &sets = shownSets(); + if (_section == Section::Featured) { + tilly += st::emojiPanHeader; + for (int c = 0, l = sets.size(); c < l; ++c) { + y = tilly; + auto &set = sets[c]; + tilly = y + featuredRowHeight(); + if (r.top() >= tilly) continue; + if (y >= r.y() + r.height()) break; + + int size = set.pack.size(); + + int widthForTitle = featuredContentWidth() - st::emojiPanHeaderLeft; + if (featuredHasAddButton(c)) { + auto add = featuredAddRect(c); + auto selected = (_selectedFeaturedSetAdd == c); + auto textBg = selected ? st::featuredStickersAdd.textBgOver : st::featuredStickersAdd.textBg; + auto textTop = (selected && _selectedFeaturedSetAdd == _pressedFeaturedSetAdd) ? st::featuredStickersAdd.downTextTop : st::featuredStickersAdd.textTop; + + App::roundRect(p, myrtlrect(add), textBg, ImageRoundRadius::Small); + p.setFont(st::featuredStickersAdd.font); + p.setPen(selected ? st::featuredStickersAdd.textFgOver : st::featuredStickersAdd.textFg); + p.drawTextLeft(add.x() - (st::featuredStickersAdd.width / 2), add.y() + textTop, width(), _addText, _addWidth); + + widthForTitle -= add.width() - (st::featuredStickersAdd.width / 2); + } + + auto titleText = set.title; + auto titleWidth = st::featuredStickersHeaderFont->width(titleText); + if (titleWidth > widthForTitle) { + titleText = st::featuredStickersHeaderFont->elided(titleText, widthForTitle); + titleWidth = st::featuredStickersHeaderFont->width(titleText); + } + p.setFont(st::featuredStickersHeaderFont); + p.setPen(st::featuredStickersHeaderFg); + p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersHeaderTop, width(), titleText, titleWidth); + + p.setFont(st::featuredStickersSubheaderFont); + p.setPen(st::featuredStickersSubheaderFg); + p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersSubheaderTop, width(), lng_stickers_count(lt_count, size)); + + y += st::featuredStickersHeader; + if (y >= r.y() + r.height()) break; + + for (int j = fromcol; j < tocol; ++j) { + int index = j; + if (index >= size) break; + + paintSticker(p, set, y, index); + } + } + } else { + for (int c = 0, l = sets.size(); c < l; ++c) { + y = tilly; + auto &set = sets[c]; + int32 size = set.pack.size(); + int32 rows = (size / StickerPanPerRow) + ((size % StickerPanPerRow) ? 1 : 0); + tilly = y + st::emojiPanHeader + (rows * st::stickerPanSize.height()); + if (r.y() >= tilly) continue; + + bool special = (set.flags & MTPDstickerSet::Flag::f_official); + y += st::emojiPanHeader; + if (y >= r.y() + r.height()) break; + + int fromrow = floorclamp(r.y() - y, st::stickerPanSize.height(), 0, rows); + int torow = ceilclamp(r.y() + r.height() - y, st::stickerPanSize.height(), 0, rows); + for (int i = fromrow; i < torow; ++i) { + for (int j = fromcol; j < tocol; ++j) { + int index = i * StickerPanPerRow + j; + if (index >= size) break; + + paintSticker(p, set, y, index); + } + } + } + } +} + +void StickerPanInner::paintSticker(Painter &p, Set &set, int y, int index) { + float64 hover = set.hovers[index]; + + auto sticker = set.pack[index]; + if (!sticker->sticker()) return; + + int row = (index / StickerPanPerRow), col = (index % StickerPanPerRow); + + QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), y + row * st::stickerPanSize.height()); + if (hover > 0) { + p.setOpacity(hover); + QPoint tl(pos); + if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width()); + App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); + p.setOpacity(1); + } + + bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); + if (goodThumb) { + sticker->thumb->load(); + } else { + sticker->checkSticker(); + } + + float64 coef = qMin((st::stickerPanSize.width() - st::buttonRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::buttonRadius * 2) / float64(sticker->dimensions.height())); + if (coef > 1) coef = 1; + int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height()); + if (w < 1) w = 1; + if (h < 1) h = 1; + QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); + if (goodThumb) { + p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h)); + } else if (!sticker->sticker()->img->isNull()) { + p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h)); + } + + if (hover > 0 && set.id == Stickers::RecentSetId && _custom.at(index)) { + float64 xHover = set.hovers[set.pack.size() + index]; + + QPoint xPos = pos + QPoint(st::stickerPanSize.width() - st::stickerPanDelete.pxWidth(), 0); + p.setOpacity(hover * (xHover + (1 - xHover) * st::stickerPanDeleteOpacity)); + p.drawSpriteLeft(xPos, width(), st::stickerPanDelete); + p.setOpacity(1); + } +} + +bool StickerPanInner::featuredHasAddButton(int index) const { + if (index < 0 || index >= _featuredSets.size()) { + return false; + } + auto flags = _featuredSets[index].flags; + return !(flags & MTPDstickerSet::Flag::f_installed) || (flags & MTPDstickerSet::Flag::f_archived); +} + +int StickerPanInner::featuredContentWidth() const { + return st::stickerPanPadding + (StickerPanPerRow * st::stickerPanSize.width()); +} + +QRect StickerPanInner::featuredAddRect(int index) const { + int addw = _addWidth - st::featuredStickersAdd.width; + int addh = st::featuredStickersAdd.height; + int addx = featuredContentWidth() - addw; + int addy = st::emojiPanHeader + index * featuredRowHeight() + st::featuredStickersAddTop; + return QRect(addx, addy, addw, addh); +} + +void StickerPanInner::mousePressEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); + + _pressed = _selected; + _pressedFeaturedSet = _selectedFeaturedSet; + _pressedFeaturedSetAdd = _selectedFeaturedSetAdd; + ClickHandler::pressed(); + _previewTimer.start(QApplication::startDragTime()); +} + +void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { + _previewTimer.stop(); + + auto pressed = _pressed; + _pressed = -1; + auto pressedFeaturedSet = _pressedFeaturedSet; + _pressedFeaturedSet = -1; + auto pressedFeaturedSetAdd = _pressedFeaturedSetAdd; + if (_pressedFeaturedSetAdd != _selectedFeaturedSetAdd) { + update(); + } + _pressedFeaturedSetAdd = -1; + + ClickHandlerPtr activated = ClickHandler::unpressed(); + + _lastMousePos = e->globalPos(); + updateSelected(); + + if (_previewShown) { + _previewShown = false; + return; + } + + if (showingInlineItems()) { + if (_selected < 0 || _selected != pressed || !activated) { + return; + } + + if (dynamic_cast(activated.data())) { + int row = _selected / MatrixRowShift, column = _selected % MatrixRowShift; + selectInlineResult(row, column); + } else { + App::activateClickHandler(activated, e->button()); + } + return; + } + + auto &sets = shownSets(); + if (_selected >= 0 && _selected < MatrixRowShift * sets.size() && _selected == pressed) { + int tab = (_selected / MatrixRowShift), sel = _selected % MatrixRowShift; + if (sets[tab].id == Stickers::RecentSetId && sel >= sets[tab].pack.size() && sel < sets[tab].pack.size() * 2 && _custom.at(sel - sets[tab].pack.size())) { + removeRecentSticker(tab, sel - sets[tab].pack.size()); + return; + } + if (sel < sets[tab].pack.size()) { + emit selected(sets[tab].pack[sel]); + } + } else if (_selectedFeaturedSet >= 0 && _selectedFeaturedSet < sets.size() && _selectedFeaturedSet == pressedFeaturedSet) { + emit displaySet(sets[_selectedFeaturedSet].id); + } else if (_selectedFeaturedSetAdd >= 0 && _selectedFeaturedSetAdd < sets.size() && _selectedFeaturedSetAdd == pressedFeaturedSetAdd) { + emit installSet(sets[_selectedFeaturedSetAdd].id); + } +} + +void StickerPanInner::selectInlineResult(int row, int column) { + if (row >= _inlineRows.size() || column >= _inlineRows.at(row).items.size()) { + return; + } + + auto item = _inlineRows[row].items[column]; + if (auto photo = item->getPhoto()) { + if (photo->medium->loaded() || photo->thumb->loaded()) { + emit selected(photo); + } else if (!photo->medium->loading()) { + photo->thumb->loadEvenCancelled(); + photo->medium->loadEvenCancelled(); + } + } else if (auto document = item->getDocument()) { + if (document->loaded()) { + emit selected(document); + } else if (document->loading()) { + document->cancel(); + } else { + DocumentOpenClickHandler::doOpen(document, ActionOnLoadNone); + } + } else if (auto inlineResult = item->getResult()) { + if (inlineResult->onChoose(item)) { + emit selected(inlineResult, _inlineBot); + } + } +} + +void StickerPanInner::removeRecentSticker(int tab, int index) { + if (_section != Section::Stickers || tab >= _mySets.size() || _mySets[tab].id != Stickers::RecentSetId) { + return; + } + + clearSelection(true); + bool refresh = false; + auto sticker = _mySets[tab].pack[index]; + auto &recent = cGetRecentStickers(); + for (int32 i = 0, l = recent.size(); i < l; ++i) { + if (recent.at(i).first == sticker) { + recent.removeAt(i); + Local::writeUserSettings(); + refresh = true; + break; + } + } + auto &sets = Global::RefStickerSets(); + auto it = sets.find(Stickers::CustomSetId); + if (it != sets.cend()) { + for (int i = 0, l = it->stickers.size(); i < l; ++i) { + if (it->stickers.at(i) == sticker) { + it->stickers.removeAt(i); + if (it->stickers.isEmpty()) { + sets.erase(it); + } + Local::writeInstalledStickers(); + refresh = true; + break; + } + } + } + if (refresh) { + refreshRecentStickers(); + updateSelected(); + update(); + } +} + +void StickerPanInner::mouseMoveEvent(QMouseEvent *e) { + _lastMousePos = e->globalPos(); + updateSelected(); +} + +void StickerPanInner::leaveEvent(QEvent *e) { + clearSelection(); +} + +void StickerPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { + clearSelection(); +} + +void StickerPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { + _lastMousePos = QCursor::pos(); + updateSelected(); +} + +bool StickerPanInner::showSectionIcons() const { + return !inlineResultsShown(); +} + +void StickerPanInner::clearSelection(bool fast) { + _lastMousePos = mapToGlobal(QPoint(-10, -10)); + if (fast) { + if (showingInlineItems()) { + if (_selected >= 0) { + int srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift; + t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); + ClickHandler::clearActive(_inlineRows.at(srow).items.at(scol)); + setCursor(style::cur_default); + } + _selected = _pressed = -1; + return; + } + + auto &sets = shownSets(); + for (auto i = _animations.cbegin(); i != _animations.cend(); ++i) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + sets[tab].hovers[sel] = 0; + } + _animations.clear(); + if (_selected >= 0) { + int index = qAbs(_selected), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + if (index >= 0 && tab < sets.size() && sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + sets[tab].pack.size()) { + sets[tab].hovers[sel] = 0; + sel -= sets[tab].pack.size(); + } + sets[tab].hovers[sel] = 0; + } + if (_pressed >= 0) { + int index = qAbs(_pressed), tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + if (index >= 0 && tab < sets.size() && sets[tab].id == Stickers::RecentSetId && sel >= tab * MatrixRowShift + sets[tab].pack.size()) { + sets[tab].hovers[sel] = 0; + sel -= sets[tab].pack.size(); + } + sets[tab].hovers[sel] = 0; + } + _selected = _pressed = -1; + _selectedFeaturedSet = _pressedFeaturedSet = -1; + _selectedFeaturedSetAdd = _pressedFeaturedSetAdd = -1; + _a_selected.stop(); + update(); + } else { + updateSelected(); + } +} + +void StickerPanInner::hideFinish(bool completely) { + if (completely) { + auto itemForget = [](const InlineItem *item) { + if (auto document = item->getDocument()) { + document->forget(); + } + if (auto photo = item->getPhoto()) { + photo->forget(); + } + if (auto result = item->getResult()) { + result->forget(); + } + }; + clearInlineRows(false); + for_const (auto item, _gifLayouts) { + itemForget(item); + } + for_const (auto item, _inlineLayouts) { + itemForget(item); + } + } + if (_setGifCommand && _section == Section::Gifs) { + App::insertBotCommand(qsl(""), true); + } + _setGifCommand = false; + + // Reset to the recent stickers section. + if (_section == Section::Featured) { + _section = Section::Stickers; + } +} + +void StickerPanInner::refreshStickers() { + clearSelection(true); + + _mySets.clear(); + _mySets.reserve(Global::StickerSetsOrder().size() + 1); + + refreshRecentStickers(false); + for_const (auto setId, Global::StickerSetsOrder()) { + appendSet(_mySets, setId); + } + + _featuredSets.clear(); + _featuredSets.reserve(Global::FeaturedStickerSetsOrder().size()); + + for_const (auto setId, Global::FeaturedStickerSetsOrder()) { + appendSet(_featuredSets, setId); + } + + if (_section == Section::Stickers) { + int h = countHeight(); + if (h != height()) resize(width(), h); + + _settings.setVisible(_mySets.isEmpty()); + } else { + _settings.hide(); + } + + emit refreshIcons(); + + updateSelected(); +} + +bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) { + InlineItem *layout = nullptr; + if (savedGif) { + layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size()); + } else if (result) { + layout = layoutPrepareInlineResult(result, (_inlineRows.size() * MatrixRowShift) + row.items.size()); + } + if (!layout) return false; + + layout->preload(); + if (inlineRowFinalize(row, sumWidth, layout->isFullLine())) { + layout->setPosition(_inlineRows.size() * MatrixRowShift); + } + + sumWidth += layout->maxWidth(); + if (!row.items.isEmpty() && row.items.back()->hasRightSkip()) { + sumWidth += st::inlineResultsSkip; + } + + row.items.push_back(layout); + return true; +} + +bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) { + if (row.items.isEmpty()) return false; + + bool full = (row.items.size() >= InlineItemsMaxPerRow); + bool big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft); + if (full || big || force) { + _inlineRows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0)); + row = InlineRow(); + row.items.reserve(InlineItemsMaxPerRow); + sumWidth = 0; + return true; + } + return false; +} + +void StickerPanInner::refreshSavedGifs() { + if (_section == Section::Gifs) { + _settings.hide(); + clearInlineRows(false); + + auto &saved = cSavedGifs(); + if (saved.isEmpty()) { + showStickerSet(Stickers::RecentSetId); + return; + } else { + _inlineRows.reserve(saved.size()); + InlineRow row; + row.items.reserve(InlineItemsMaxPerRow); + int sumWidth = 0; + for_const (auto &gif, saved) { + inlineRowsAddItem(gif, 0, row, sumWidth); + } + inlineRowFinalize(row, sumWidth, true); + } + deleteUnusedGifLayouts(); + + int32 h = countHeight(); + if (h != height()) resize(width(), h); + + update(); + } + emit refreshIcons(); + + updateSelected(); +} + +void StickerPanInner::inlineBotChanged() { + _setGifCommand = false; + refreshInlineRows(nullptr, nullptr, true); +} + +void StickerPanInner::clearInlineRows(bool resultsDeleted) { + if (resultsDeleted) { + if (showingInlineItems()) { + _selected = _pressed = -1; + } + } else { + if (showingInlineItems()) { + clearSelection(true); + } + for_const (auto &row, _inlineRows) { + for_const (auto &item, row.items) { + item->setPosition(-1); + } + } + } + _inlineRows.clear(); +} + +InlineItem *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) { + auto i = _gifLayouts.constFind(doc); + if (i == _gifLayouts.cend()) { + if (auto layout = InlineItem::createLayoutGif(doc)) { + i = _gifLayouts.insert(doc, layout.release()); + i.value()->initDimensions(); + } else { + return nullptr; + } + } + if (!i.value()->maxWidth()) return nullptr; + + i.value()->setPosition(position); + return i.value(); +} + +InlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *result, int32 position) { + auto i = _inlineLayouts.constFind(result); + if (i == _inlineLayouts.cend()) { + if (auto layout = InlineItem::createLayout(result, _inlineWithThumb)) { + i = _inlineLayouts.insert(result, layout.release()); + i.value()->initDimensions(); + } else { + return nullptr; + } + } + if (!i.value()->maxWidth()) return nullptr; + + i.value()->setPosition(position); + return i.value(); +} + +void StickerPanInner::deleteUnusedGifLayouts() { + if (_inlineRows.isEmpty() || _section != Section::Gifs) { // delete all + for_const (auto item, _gifLayouts) { + delete item; + } + _gifLayouts.clear(); + } else { + for (auto i = _gifLayouts.begin(); i != _gifLayouts.cend();) { + if (i.value()->position() < 0) { + delete i.value(); + i = _gifLayouts.erase(i); + } else { + ++i; + } + } + } +} + +void StickerPanInner::deleteUnusedInlineLayouts() { + if (_inlineRows.isEmpty() || _section == Section::Gifs) { // delete all + for_const (auto item, _inlineLayouts) { + delete item; + } + _inlineLayouts.clear(); + } else { + for (auto i = _inlineLayouts.begin(); i != _inlineLayouts.cend();) { + if (i.value()->position() < 0) { + delete i.value(); + i = _inlineLayouts.erase(i); + } else { + ++i; + } + } + } +} + +StickerPanInner::InlineRow &StickerPanInner::layoutInlineRow(InlineRow &row, int32 sumWidth) { + int32 count = row.items.size(); + t_assert(count <= InlineItemsMaxPerRow); + + // enumerate items in the order of growing maxWidth() + // for that sort item indices by maxWidth() + int indices[InlineItemsMaxPerRow]; + for (int i = 0; i < count; ++i) { + indices[i] = i; + } + std::sort(indices, indices + count, [&row](int a, int b) -> bool { + return row.items.at(a)->maxWidth() < row.items.at(b)->maxWidth(); + }); + + row.height = 0; + int availw = width() - st::inlineResultsLeft; + for (int i = 0; i < count; ++i) { + int index = indices[i]; + int w = sumWidth ? (row.items.at(index)->maxWidth() * availw / sumWidth) : row.items.at(index)->maxWidth(); + int actualw = qMax(w, int(st::inlineResultsMinWidth)); + row.height = qMax(row.height, row.items.at(index)->resizeGetHeight(actualw)); + if (sumWidth) { + availw -= actualw; + sumWidth -= row.items.at(index)->maxWidth(); + if (index > 0 && row.items.at(index - 1)->hasRightSkip()) { + availw -= st::inlineResultsSkip; + sumWidth -= st::inlineResultsSkip; + } + } + } + return row; +} + +void StickerPanInner::preloadImages() { + if (showingInlineItems()) { + for (int32 row = 0, rows = _inlineRows.size(); row < rows; ++row) { + for (int32 col = 0, cols = _inlineRows.at(row).items.size(); col < cols; ++col) { + _inlineRows.at(row).items.at(col)->preload(); + } + } + return; + } + + auto &sets = shownSets(); + for (int i = 0, l = sets.size(), k = 0; i < l; ++i) { + int count = sets[i].pack.size(); + if (_section == Section::Featured) { + accumulate_min(count, static_cast(StickerPanPerRow)); + } + for (int j = 0; j != count; ++j) { + if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break; + + auto sticker = sets.at(i).pack.at(j); + if (!sticker || !sticker->sticker()) continue; + + bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); + if (goodThumb) { + sticker->thumb->load(); + } else { + sticker->automaticLoad(0); + } + } + if (k > StickerPanPerRow * (StickerPanPerRow + 1)) break; + } +} + +uint64 StickerPanInner::currentSet(int yOffset) const { + if (showingInlineItems()) { + return Stickers::NoneSetId; + } else if (_section == Section::Featured) { + return Stickers::FeaturedSetId; + } + + int y, ytill = 0; + for (int i = 0, l = _mySets.size(); i < l; ++i) { + int cnt = _mySets[i].pack.size(); + y = ytill; + ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); + if (yOffset < ytill) { + return _mySets[i].id; + } + } + return _mySets.isEmpty() ? Stickers::RecentSetId : _mySets.back().id; +} + +void StickerPanInner::hideInlineRowsPanel() { + clearInlineRows(false); + if (showingInlineItems()) { + _section = cShowingSavedGifs() ? Section::Gifs : Section::Inlines; + if (_section == Section::Gifs) { + refreshSavedGifs(); + emit scrollToY(0); + emit scrollUpdated(); + } else { + showStickerSet(Stickers::RecentSetId); + } + } +} + +void StickerPanInner::clearInlineRowsPanel() { + clearInlineRows(false); +} + +void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) { + if (!entry || entry->switchPmText.isEmpty()) { + _switchPmButton.reset(); + _switchPmStartToken.clear(); + } else { + if (!_switchPmButton) { + _switchPmButton = std_::make_unique(this, QString(), st::switchPmButton); + _switchPmButton->show(); + _switchPmButton->move(st::inlineResultsLeft, st::emojiPanHeader); + connect(_switchPmButton.get(), SIGNAL(clicked()), this, SLOT(onSwitchPm())); + } + _switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper() + _switchPmStartToken = entry->switchPmStartToken; + } + update(); +} + +int StickerPanInner::refreshInlineRows(UserData *bot, const InlineCacheEntry *entry, bool resultsDeleted) { + _inlineBot = bot; + refreshSwitchPmButton(entry); + auto clearResults = [this, entry]() { + if (!entry) { + return true; + } + if (entry->results.isEmpty() && entry->switchPmText.isEmpty()) { + if (!_inlineBot || _inlineBot->username != cInlineGifBotUsername()) { + return true; + } + } + return false; + }; + if (clearResults()) { + if (resultsDeleted) { + clearInlineRows(true); + deleteUnusedInlineLayouts(); + } + emit emptyInlineRows(); + return 0; + } + + clearSelection(true); + + t_assert(_inlineBot != 0); + _inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username)); + + _section = Section::Inlines; + _settings.hide(); + + int32 count = entry->results.size(), from = validateExistingInlineRows(entry->results), added = 0; + + if (count) { + _inlineRows.reserve(count); + InlineRow row; + row.items.reserve(InlineItemsMaxPerRow); + int32 sumWidth = 0; + for (int32 i = from; i < count; ++i) { + if (inlineRowsAddItem(0, entry->results.at(i), row, sumWidth)) { + ++added; + } + } + inlineRowFinalize(row, sumWidth, true); + } + + int32 h = countHeight(); + if (h != height()) resize(width(), h); + update(); + + emit refreshIcons(); + + _lastMousePos = QCursor::pos(); + updateSelected(); + + return added; +} + +int StickerPanInner::validateExistingInlineRows(const InlineResults &results) { + int count = results.size(), until = 0, untilrow = 0, untilcol = 0; + for (; until < count;) { + if (untilrow >= _inlineRows.size() || _inlineRows.at(untilrow).items.at(untilcol)->getResult() != results.at(until)) { + break; + } + ++until; + if (++untilcol == _inlineRows.at(untilrow).items.size()) { + ++untilrow; + untilcol = 0; + } + } + if (until == count) { // all items are layed out + if (untilrow == _inlineRows.size()) { // nothing changed + return until; + } + + for (int i = untilrow, l = _inlineRows.size(), skip = untilcol; i < l; ++i) { + for (int j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { + if (skip) { + --skip; + } else { + _inlineRows.at(i).items.at(j)->setPosition(-1); + } + } + } + if (!untilcol) { // all good rows are filled + _inlineRows.resize(untilrow); + return until; + } + _inlineRows.resize(untilrow + 1); + _inlineRows[untilrow].items.resize(untilcol); + _inlineRows[untilrow] = layoutInlineRow(_inlineRows[untilrow]); + return until; + } + if (untilrow && !untilcol) { // remove last row, maybe it is not full + --untilrow; + untilcol = _inlineRows.at(untilrow).items.size(); + } + until -= untilcol; + + for (int i = untilrow, l = _inlineRows.size(); i < l; ++i) { + for (int j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) { + _inlineRows.at(i).items.at(j)->setPosition(-1); + } + } + _inlineRows.resize(untilrow); + + if (_inlineRows.isEmpty()) { + _inlineWithThumb = false; + for (int i = until; i < count; ++i) { + if (results.at(i)->hasThumbDisplay()) { + _inlineWithThumb = true; + break; + } + } + } + return until; +} + +void StickerPanInner::notify_inlineItemLayoutChanged(const InlineItem *layout) { + if (_selected < 0 || !showingInlineItems()) { + return; + } + + int row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; + if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { + if (layout == _inlineRows.at(row).items.at(col)) { + updateSelected(); + } + } +} + +void StickerPanInner::ui_repaintInlineItem(const InlineItem *layout) { + uint64 ms = getms(); + if (_lastScrolled + 100 <= ms) { + update(); + } else { + _updateInlineItems.start(_lastScrolled + 100 - ms); + } +} + +bool StickerPanInner::ui_isInlineItemVisible(const InlineItem *layout) { + int32 position = layout->position(); + if (!showingInlineItems() || position < 0) { + return false; + } + + int row = position / MatrixRowShift, col = position % MatrixRowShift; + t_assert((row < _inlineRows.size()) && (col < _inlineRows[row].items.size())); + + auto &inlineItems = _inlineRows[row].items; + int top = st::emojiPanHeader; + for (int32 i = 0; i < row; ++i) { + top += _inlineRows.at(i).height; + } + + return (top < _top + _maxHeight) && (top + _inlineRows.at(row).items.at(col)->height() > _top); +} + +bool StickerPanInner::ui_isInlineItemBeingChosen() { + return showingInlineItems(); +} + +void StickerPanInner::appendSet(Sets &to, uint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it == sets.cend() || (it->flags & MTPDstickerSet::Flag::f_archived) || it->stickers.isEmpty()) return; + + to.push_back(Set(it->id, it->flags, it->title, it->stickers.size() + 1, it->stickers)); +} + +void StickerPanInner::refreshRecent() { + if (_section == Section::Gifs) { + refreshSavedGifs(); + } else if (_section == Section::Stickers) { + refreshRecentStickers(); + } +} + +void StickerPanInner::refreshRecentStickers(bool performResize) { + _custom.clear(); + clearSelection(true); + auto &sets = Global::StickerSets(); + auto &recent = cGetRecentStickers(); + auto customIt = sets.constFind(Stickers::CustomSetId); + auto cloudIt = sets.constFind(Stickers::CloudRecentSetId); + if (recent.isEmpty() + && (customIt == sets.cend() || customIt->stickers.isEmpty()) + && (cloudIt == sets.cend() || cloudIt->stickers.isEmpty())) { + if (!_mySets.isEmpty() && _mySets.at(0).id == Stickers::RecentSetId) { + _mySets.pop_front(); + } + } else { + StickerPack recentPack; + int customCnt = (customIt == sets.cend()) ? 0 : customIt->stickers.size(); + int cloudCnt = (cloudIt == sets.cend()) ? 0 : cloudIt->stickers.size(); + recentPack.reserve(cloudCnt + recent.size() + customCnt); + _custom.reserve(cloudCnt + recent.size() + customCnt); + if (cloudCnt > 0) { + for_const (auto sticker, cloudIt->stickers) { + recentPack.push_back(sticker); + _custom.push_back(false); + } + } + for_const (auto &recentSticker, recent) { + auto sticker = recentSticker.first; + recentPack.push_back(sticker); + _custom.push_back(false); + } + if (customCnt > 0) { + for_const (auto &sticker, customIt->stickers) { + auto index = recentPack.indexOf(sticker); + if (index >= cloudCnt) { + _custom[index] = true; // mark stickers from recent as custom + } else { + recentPack.push_back(sticker); + _custom.push_back(true); + } + } + } + if (_mySets.isEmpty() || _mySets.at(0).id != Stickers::RecentSetId) { + _mySets.push_back(Set(Stickers::RecentSetId, MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special, lang(lng_recent_stickers), recentPack.size() * 2, recentPack)); + } else { + _mySets[0].pack = recentPack; + _mySets[0].hovers.resize(recentPack.size() * 2); + } + } + + if (performResize && (_section == Section::Stickers || _section == Section::Featured)) { + int32 h = countHeight(); + if (h != height()) { + resize(width(), h); + emit needRefreshPanels(); + } + + updateSelected(); + } +} + +void StickerPanInner::fillIcons(QList &icons) { + icons.clear(); + icons.reserve(_mySets.size() + 1); + if (!cSavedGifs().isEmpty()) { + icons.push_back(StickerIcon(Stickers::NoneSetId)); + } + if (Global::FeaturedStickerSetsUnreadCount()) { + icons.push_back(StickerIcon(Stickers::FeaturedSetId)); + } + + if (!_mySets.isEmpty()) { + int i = 0; + if (_mySets[0].id == Stickers::RecentSetId) { + ++i; + icons.push_back(StickerIcon(Stickers::RecentSetId)); + } + for (int l = _mySets.size(); i < l; ++i) { + auto s = _mySets[i].pack[0]; + int32 availw = st::rbEmoji.width - 2 * st::stickerIconPadding, availh = st::rbEmoji.height - 2 * st::stickerIconPadding; + int32 thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1; + if (availw * thumbh > availh * thumbw) { + pixh = availh; + pixw = (pixh * thumbw) / thumbh; + } else { + pixw = availw; + pixh = thumbw ? ((pixw * thumbh) / thumbw) : 1; + } + if (pixw < 1) pixw = 1; + if (pixh < 1) pixh = 1; + icons.push_back(StickerIcon(_mySets[i].id, s, pixw, pixh)); + } + } + + if (!Global::FeaturedStickerSetsUnreadCount() && !Global::FeaturedStickerSetsOrder().empty()) { + icons.push_back(StickerIcon(Stickers::FeaturedSetId)); + } +} + +void StickerPanInner::fillPanels(QVector &panels) { + for (int32 i = 0; i < panels.size(); ++i) { + panels.at(i)->hide(); + panels.at(i)->deleteLater(); + } + panels.clear(); + + if (_section != Section::Stickers) { + auto title = [this]() -> QString { + if (_section == Section::Gifs) { + return lang(lng_saved_gifs); + } else if (_section == Section::Inlines) { + return _inlineBotTitle; + } + return lang(lng_stickers_featured); + }; + panels.push_back(new EmojiPanel(parentWidget(), title(), Stickers::NoneSetId, true, 0)); + panels.back()->show(); + return; + } + + if (_mySets.isEmpty()) return; + + int y = 0; + panels.reserve(_mySets.size()); + for (int32 i = 0, l = _mySets.size(); i < l; ++i) { + bool special = (_mySets[i].flags & MTPDstickerSet::Flag::f_official); + panels.push_back(new EmojiPanel(parentWidget(), _mySets[i].title, _mySets[i].id, special, y)); + panels.back()->show(); + connect(panels.back(), SIGNAL(deleteClicked(quint64)), this, SIGNAL(removeSet(quint64))); + int cnt = _mySets[i].pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + y += h; + } +} + +void StickerPanInner::refreshPanels(QVector &panels) { + if (_section != Section::Stickers) return; + + if (panels.size() != _mySets.size()) { + return fillPanels(panels); + } + + int y = 0; + for (int i = 0, l = _mySets.size(); i < l; ++i) { + panels.at(i)->setWantedY(y); + int cnt = _mySets[i].pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + y += h; + } +} + +void StickerPanInner::updateSelected() { + if (_pressed >= 0 && !_previewShown) { + return; + } + + int selIndex = -1; + auto p = mapFromGlobal(_lastMousePos); + + if (showingInlineItems()) { + int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft; + int sy = p.y() - st::emojiPanHeader; + if (_switchPmButton) { + sy -= _switchPmButton->height() + st::inlineResultsSkip; + } + int row = -1, col = -1, sel = -1; + ClickHandlerPtr lnk; + ClickHandlerHost *lnkhost = nullptr; + HistoryCursorState cursor = HistoryDefaultCursorState; + if (sy >= 0) { + row = 0; + for (int rows = _inlineRows.size(); row < rows; ++row) { + if (sy < _inlineRows.at(row).height) { + break; + } + sy -= _inlineRows.at(row).height; + } + } + if (sx >= 0 && row >= 0 && row < _inlineRows.size()) { + auto &inlineItems = _inlineRows[row].items; + col = 0; + for (int cols = inlineItems.size(); col < cols; ++col) { + int width = inlineItems.at(col)->width(); + if (sx < width) { + break; + } + sx -= width; + if (inlineItems.at(col)->hasRightSkip()) { + sx -= st::inlineResultsSkip; + } + } + if (col < inlineItems.size()) { + sel = row * MatrixRowShift + col; + inlineItems.at(col)->getState(lnk, cursor, sx, sy); + lnkhost = inlineItems.at(col); + } else { + row = col = -1; + } + } else { + row = col = -1; + } + int srow = (_selected >= 0) ? (_selected / MatrixRowShift) : -1; + int scol = (_selected >= 0) ? (_selected % MatrixRowShift) : -1; + if (_selected != sel) { + if (srow >= 0 && scol >= 0) { + t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); + Ui::repaintInlineItem(_inlineRows.at(srow).items.at(scol)); + } + _selected = sel; + if (row >= 0 && col >= 0) { + t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size()); + Ui::repaintInlineItem(_inlineRows.at(row).items.at(col)); + } + if (_pressed >= 0 && _selected >= 0 && _pressed != _selected) { + _pressed = _selected; + if (row >= 0 && col >= 0) { + auto layout = _inlineRows.at(row).items.at(col); + if (auto previewDocument = layout->getPreviewDocument()) { + Ui::showMediaPreview(previewDocument); + } else if (auto previewPhoto = layout->getPreviewPhoto()) { + Ui::showMediaPreview(previewPhoto); + } + } + } + } + if (ClickHandler::setActive(lnk, lnkhost)) { + setCursor(lnk ? style::cur_pointer : style::cur_default); + } + return; + } + + int selectedFeaturedSet = -1; + int selectedFeaturedSetAdd = -1; + auto featured = (_section == Section::Featured); + auto &sets = shownSets(); + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; + if (featured) { + ytill += st::emojiPanHeader; + } + for (int c = 0, l = sets.size(); c < l; ++c) { + auto &set = sets[c]; + bool special = featured ? false : (set.flags & MTPDstickerSet::Flag::f_official); + + y = ytill; + if (featured) { + ytill = y + featuredRowHeight(); + } else { + int cnt = set.pack.size(); + ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height(); + } + if (p.y() >= y && p.y() < ytill) { + if (featured) { + if (p.y() < y + st::featuredStickersHeader) { + if (featuredHasAddButton(c) && myrtlrect(featuredAddRect(c)).contains(p.x(), p.y())) { + selectedFeaturedSetAdd = c; + } else { + selectedFeaturedSet = c; + } + break; + } + y += st::featuredStickersHeader; + } else { + y += st::emojiPanHeader; + } + if (p.y() >= y && sx >= 0 && sx < StickerPanPerRow * st::stickerPanSize.width()) { + auto rowIndex = qFloor((p.y() - y) / st::stickerPanSize.height()); + if (!featured || !rowIndex) { + selIndex = rowIndex * StickerPanPerRow + qFloor(sx / st::stickerPanSize.width()); + if (selIndex >= set.pack.size()) { + selIndex = -1; + } else { + if (set.id == Stickers::RecentSetId && _custom[selIndex]) { + int inx = sx - (selIndex % StickerPanPerRow) * st::stickerPanSize.width(), iny = p.y() - y - ((selIndex / StickerPanPerRow) * st::stickerPanSize.height()); + if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) { + selIndex += set.pack.size(); + } + } + selIndex += c * MatrixRowShift; + } + } + } + break; + } + } + + bool startanim = false; + int oldSel = _selected, oldSelTab = oldSel / MatrixRowShift, xOldSel = -1, newSel = selIndex, newSelTab = newSel / MatrixRowShift, xNewSel = -1; + if (oldSel >= 0 && oldSelTab < sets.size() && sets[oldSelTab].id == Stickers::RecentSetId && oldSel >= oldSelTab * MatrixRowShift + sets[oldSelTab].pack.size()) { + xOldSel = oldSel; + oldSel -= sets[oldSelTab].pack.size(); + } + if (newSel >= 0 && newSelTab < sets.size() && sets[newSelTab].id == Stickers::RecentSetId && newSel >= newSelTab * MatrixRowShift + sets[newSelTab].pack.size()) { + xNewSel = newSel; + newSel -= sets[newSelTab].pack.size(); + } + if (newSel != oldSel || selectedFeaturedSet != _selectedFeaturedSet || selectedFeaturedSetAdd != _selectedFeaturedSetAdd) { + setCursor((newSel >= 0 || selectedFeaturedSet >= 0 || selectedFeaturedSetAdd >= 0) ? style::cur_pointer : style::cur_default); + } + if (newSel != oldSel) { + if (oldSel >= 0) { + _animations.remove(oldSel + 1); + if (_animations.find(-oldSel - 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(-oldSel - 1, getms()); + } + } + if (newSel >= 0) { + _animations.remove(-newSel - 1); + if (_animations.find(newSel + 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(newSel + 1, getms()); + } + } + } + if (selectedFeaturedSet != _selectedFeaturedSet) { + _selectedFeaturedSet = selectedFeaturedSet; + } + if (selectedFeaturedSetAdd != _selectedFeaturedSetAdd) { + _selectedFeaturedSetAdd = selectedFeaturedSetAdd; + update(); + } + if (xNewSel != xOldSel) { + if (xOldSel >= 0) { + _animations.remove(xOldSel + 1); + if (_animations.find(-xOldSel - 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(-xOldSel - 1, getms()); + } + } + if (xNewSel >= 0) { + _animations.remove(-xNewSel - 1); + if (_animations.find(xNewSel + 1) == _animations.end()) { + if (_animations.isEmpty()) startanim = true; + _animations.insert(xNewSel + 1, getms()); + } + } + } + _selected = selIndex; + if (_pressed >= 0 && _selected >= 0 && _pressed != _selected) { + _pressed = _selected; + if (newSel >= 0 && xNewSel < 0) { + Ui::showMediaPreview(sets.at(newSelTab).pack.at(newSel % MatrixRowShift)); + } + } + if (startanim && !_a_selected.animating()) _a_selected.start(); +} + +void StickerPanInner::onSettings() { + Ui::showLayer(new StickersBox()); +} + +void StickerPanInner::onPreview() { + if (_pressed < 0) return; + if (showingInlineItems()) { + int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; + if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { + auto layout = _inlineRows.at(row).items.at(col); + if (auto previewDocument = layout->getPreviewDocument()) { + Ui::showMediaPreview(previewDocument); + _previewShown = true; + } else if (auto previewPhoto = layout->getPreviewPhoto()) { + Ui::showMediaPreview(previewPhoto); + _previewShown = true; + } + } + } else { + auto &sets = shownSets(); + if (_pressed < MatrixRowShift * sets.size()) { + int tab = (_pressed / MatrixRowShift), sel = _pressed % MatrixRowShift; + if (sel < sets[tab].pack.size()) { + Ui::showMediaPreview(sets[tab].pack[sel]); + _previewShown = true; + } + } + } +} + +void StickerPanInner::onUpdateInlineItems() { + if (!showingInlineItems()) return; + + uint64 ms = getms(); + if (_lastScrolled + 100 <= ms) { + update(); + } else { + _updateInlineItems.start(_lastScrolled + 100 - ms); + } +} + +void StickerPanInner::onSwitchPm() { + if (_inlineBot && _inlineBot->botInfo) { + _inlineBot->botInfo->startToken = _switchPmStartToken; + Ui::showPeerHistory(_inlineBot, ShowAndStartBotMsgId); + } +} + +void StickerPanInner::step_selected(uint64 ms, bool timer) { + QRegion toUpdate; + auto &sets = shownSets(); + for (auto i = _animations.begin(); i != _animations.end();) { + int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (dt >= 1) { + sets[tab].hovers[sel] = (i.key() > 0) ? 1 : 0; + i = _animations.erase(i); + } else { + sets[tab].hovers[sel] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + toUpdate += stickerRect(tab, sel); + } + if (timer) rtlupdate(toUpdate.boundingRect()); + if (_animations.isEmpty()) _a_selected.stop(); +} + +void StickerPanInner::showStickerSet(uint64 setId) { + clearSelection(true); + + if (setId == Stickers::NoneSetId) { + if (!showingInlineItems()) { + _section = Section::Gifs; + cSetShowingSavedGifs(true); + emit saveConfigDelayed(SaveRecentEmojisTimeout); + } + refreshSavedGifs(); + emit scrollToY(0); + emit scrollUpdated(); + showFinish(); + return; + } + + if (showingInlineItems()) { + if (_setGifCommand && _section == Section::Gifs) { + App::insertBotCommand(qsl(""), true); + } + _setGifCommand = false; + + cSetShowingSavedGifs(false); + emit saveConfigDelayed(SaveRecentEmojisTimeout); + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); + } + + if (setId == Stickers::FeaturedSetId) { + if (_section != Section::Featured) { + _section = Section::Featured; + + refreshRecentStickers(true); + emit refreshIcons(); + update(); + } + + emit scrollToY(0); + emit scrollUpdated(); + return; + } + + bool needRefresh = (_section != Section::Stickers); + if (needRefresh) { + _section = Section::Stickers; + refreshRecentStickers(true); + } + + int32 y = 0; + for (int c = 0; c < _mySets.size(); ++c) { + if (_mySets.at(c).id == setId) break; + int rows = (_mySets[c].pack.size() / StickerPanPerRow) + ((_mySets[c].pack.size() % StickerPanPerRow) ? 1 : 0); + y += st::emojiPanHeader + rows * st::stickerPanSize.height(); + } + + emit scrollToY(y); + emit scrollUpdated(); + + if (needRefresh) { + emit refreshIcons(); + } + + _lastMousePos = QCursor::pos(); + + update(); +} + +void StickerPanInner::updateShowingSavedGifs() { + if (cShowingSavedGifs()) { + if (!showingInlineItems()) { + clearSelection(true); + _section = Section::Gifs; + if (_inlineRows.isEmpty()) refreshSavedGifs(); + } + } else if (!showingInlineItems()) { + clearSelection(true); + } +} + +void StickerPanInner::showFinish() { + if (_section == Section::Gifs) { + _setGifCommand = App::insertBotCommand('@' + cInlineGifBotUsername(), true); + } +} + +EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent) +, _wantedY(wantedY) +, _setId(setId) +, _special(special) +, _deleteVisible(false) +, _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // Stickers::NoneSetId if in emoji + resize(st::emojiPanWidth, st::emojiPanHeader); + setMouseTracking(true); + setFocusPolicy(Qt::NoFocus); + setText(text); + if (_delete) { + _delete->hide(); + _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::notifyClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); + connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); + } +} + +void EmojiPanel::onDelete() { + emit deleteClicked(_setId); +} + +void EmojiPanel::setText(const QString &text) { + _fullText = text; + updateText(); +} + +void EmojiPanel::updateText() { + int32 availw = st::emojiPanWidth - st::emojiPanHeaderLeft * 2; + if (_deleteVisible) { + if (!_special && _setId != Stickers::NoneSetId) { + availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft; + } + } else { + auto switchText = ([this]() { + if (_setId != Stickers::NoneSetId) { + return lang(lng_switch_emoji); + } + if (cSavedGifs().isEmpty()) { + return lang(lng_switch_stickers); + } + return lang(lng_switch_stickers_gifs); + })(); + availw -= st::emojiSwitchSkip + st::emojiPanHeaderFont->width(switchText); + } + _text = st::emojiPanHeaderFont->elided(_fullText, availw); + update(); +} + +void EmojiPanel::setDeleteVisible(bool isVisible) { + if (_deleteVisible != isVisible) { + _deleteVisible = isVisible; + updateText(); + if (_delete) { + _delete->setVisible(_deleteVisible); + } + } +} + +void EmojiPanel::mousePressEvent(QMouseEvent *e) { + emit mousePressed(); +} + +void EmojiPanel::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_deleteVisible) { + p.fillRect(0, 0, width(), st::emojiPanHeader, st::emojiPanHeaderBg->b); + } + p.setFont(st::emojiPanHeaderFont); + p.setPen(st::emojiPanHeaderColor); + p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); +} + +EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent) +, _toStickers(toStickers) { + setCursor(style::cur_pointer); + updateText(); +} + +void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { + if (_toStickers) { + if (inlineBotUsername.isEmpty()) { + _text = lang(cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs); + } else { + _text = '@' + inlineBotUsername; + } + } else { + _text = lang(lng_switch_emoji); + } + _textWidth = st::emojiPanHeaderFont->width(_text); + if (_toStickers && !inlineBotUsername.isEmpty()) { + int32 maxw = 0; + for (int c = 0; c < emojiTabCount; ++c) { + maxw = qMax(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c)))); + } + maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); + if (_textWidth > st::emojiPanWidth - maxw) { + _text = st::emojiPanHeaderFont->elided(_text, st::emojiPanWidth - maxw); + _textWidth = st::emojiPanHeaderFont->width(_text); + } + } + + int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); + resize(w, st::emojiPanHeader); +} + +void EmojiSwitchButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + p.setFont(st::emojiPanHeaderFont->f); + p.setPen(st::emojiSwitchColor->p); + if (_toStickers) { + p.drawTextRight(st::emojiSwitchSkip, st::emojiPanHeaderTop, width(), _text, _textWidth); + p.drawSpriteRight(QPoint(st::emojiSwitchImgSkip - st::emojiSwitchStickers.pxWidth(), (st::emojiPanHeader - st::emojiSwitchStickers.pxHeight()) / 2), width(), st::emojiSwitchStickers); + } else { + p.drawTextRight(st::emojiSwitchImgSkip - st::emojiSwitchEmoji.pxWidth(), st::emojiPanHeaderTop, width(), lang(lng_switch_emoji), _textWidth); + p.drawSpriteRight(QPoint(st::emojiSwitchSkip + _textWidth - st::emojiSwitchEmoji.pxWidth(), (st::emojiPanHeader - st::emojiSwitchEmoji.pxHeight()) / 2), width(), st::emojiSwitchEmoji); + } +} + +} // namespace internal + +EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) +, _maxHeight(st::emojiPanMaxHeight) +, _contentMaxHeight(st::emojiPanMaxHeight) +, _contentHeight(_contentMaxHeight) +, _contentHeightEmoji(_contentHeight - st::rbEmoji.height) +, _contentHeightStickers(_contentHeight - st::rbEmoji.height) +, _a_appearance(animation(this, &EmojiPan::step_appearance)) +, _shadow(st::dropdownDef.shadow) +, _recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent) +, _people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople) +, _nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature) +, _food(this , qsl("emoji_group"), dbietFood , QString(), false, st::rbEmojiFood) +, _activity(this, qsl("emoji_group"), dbietActivity, QString(), false, st::rbEmojiActivity) +, _travel(this , qsl("emoji_group"), dbietTravel , QString(), false, st::rbEmojiTravel) +, _objects(this , qsl("emoji_group"), dbietObjects , QString(), false, st::rbEmojiObjects) +, _symbols(this , qsl("emoji_group"), dbietSymbols , QString(), false, st::rbEmojiSymbols) +, _a_icons(animation(this, &EmojiPan::step_icons)) +, _a_slide(animation(this, &EmojiPan::step_slide)) +, e_scroll(this, st::emojiScroll) +, e_inner() +, e_switch(&e_scroll, true) +, s_scroll(this, st::emojiScroll) +, s_inner() +, s_switch(&s_scroll, false) { + setFocusPolicy(Qt::NoFocus); + e_scroll.setFocusPolicy(Qt::NoFocus); + e_scroll.viewport()->setFocusPolicy(Qt::NoFocus); + s_scroll.setFocusPolicy(Qt::NoFocus); + s_scroll.viewport()->setFocusPolicy(Qt::NoFocus); + + _width = st::dropdownDef.padding.left() + st::emojiPanWidth + st::dropdownDef.padding.right(); + _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); + _bottom = 0; + resize(_width, _height); + + e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + + e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); + e_scroll.setWidget(&e_inner); + s_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); + s_scroll.setWidget(&s_inner); + + e_inner.moveToLeft(0, 0, e_scroll.width()); + s_inner.moveToLeft(0, 0, s_scroll.width()); + + int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2; + int32 top = _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; + prepareTab(left, top, _width, _recent); + prepareTab(left, top, _width, _people); + prepareTab(left, top, _width, _nature); + prepareTab(left, top, _width, _food); + prepareTab(left, top, _width, _activity); + prepareTab(left, top, _width, _travel); + prepareTab(left, top, _width, _objects); + prepareTab(left, top, _width, _symbols); + e_inner.fillPanels(e_panels); + updatePanelsPositions(e_panels, 0); + + _hideTimer.setSingleShot(true); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); + + connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int))); + connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); + + connect(&s_inner, SIGNAL(scrollToY(int)), &s_scroll, SLOT(scrollToY(int))); + connect(&s_inner, SIGNAL(scrollUpdated()), this, SLOT(onScrollStickers())); + + connect(&e_scroll, SIGNAL(scrolled()), this, SLOT(onScrollEmoji())); + connect(&s_scroll, SIGNAL(scrolled()), this, SLOT(onScrollStickers())); + + connect(&e_inner, SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr))); + connect(&s_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*))); + connect(&s_inner, SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*))); + connect(&s_inner, SIGNAL(selected(InlineBots::Result*,UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*))); + + connect(&s_inner, SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows())); + + connect(&s_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + connect(&e_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + s_switch.moveToRight(0, 0, st::emojiPanWidth); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + + connect(&s_inner, SIGNAL(displaySet(quint64)), this, SLOT(onDisplaySet(quint64))); + connect(&s_inner, SIGNAL(installSet(quint64)), this, SLOT(onInstallSet(quint64))); + connect(&s_inner, SIGNAL(removeSet(quint64)), this, SLOT(onRemoveSet(quint64))); + connect(&s_inner, SIGNAL(refreshIcons()), this, SLOT(onRefreshIcons())); + connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); + connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); + + _saveConfigTimer.setSingleShot(true); + connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig())); + connect(&e_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); + connect(&s_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); + + // inline bots + _inlineRequestTimer.setSingleShot(true); + connect(&_inlineRequestTimer, SIGNAL(timeout()), this, SLOT(onInlineRequest())); + + if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { + connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); + } + + setMouseTracking(true); +// setAttribute(Qt::WA_AcceptTouchEvents); +} + +void EmojiPan::setMaxHeight(int32 h) { + _maxHeight = h; + updateContentHeight(); +} + +void EmojiPan::updateContentHeight() { + int32 h = qMin(_contentMaxHeight, _maxHeight); + int32 he = h - st::rbEmoji.height; + int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0); + if (h == _contentHeight && he == _contentHeightEmoji && hs == _contentHeightStickers) return; + + int32 was = _contentHeight, wase = _contentHeightEmoji, wass = _contentHeightStickers; + _contentHeight = h; + _contentHeightEmoji = he; + _contentHeightStickers = hs; + + _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); + + resize(_width, _height); + move(x(), _bottom - height()); + + if (was > _contentHeight || (was == _contentHeight && wass > _contentHeightStickers)) { + e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + s_inner.setMaxHeight(_contentHeightStickers); + e_inner.setMaxHeight(_contentHeightEmoji); + } else { + s_inner.setMaxHeight(_contentHeightStickers); + e_inner.setMaxHeight(_contentHeightEmoji); + e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + } + + _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; + _recent.move(_recent.x(), _iconsTop); + _people.move(_people.x(), _iconsTop); + _nature.move(_nature.x(), _iconsTop); + _food.move(_food.x(), _iconsTop); + _activity.move(_activity.x(), _iconsTop); + _travel.move(_travel.x(), _iconsTop); + _objects.move(_objects.x(), _iconsTop); + _symbols.move(_symbols.x(), _iconsTop); + + update(); +} + +void EmojiPan::prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab) { + tab.moveToLeft(left, top, _width); + left += tab.width(); + tab.setAttribute(Qt::WA_OpaquePaintEvent); + connect(&tab, SIGNAL(changed()), this, SLOT(onTabChange())); +} + +void EmojiPan::onWndActiveChanged() { + if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { + leaveEvent(0); + } +} + +void EmojiPan::onSaveConfig() { + Local::writeUserSettings(); +} + +void EmojiPan::onSaveConfigDelayed(int32 delay) { + _saveConfigTimer.start(delay); +} + +void EmojiPan::paintStickerSettingsIcon(Painter &p) const { + int settingsLeft = _iconsLeft + 7 * st::rbEmoji.width; + p.drawSpriteLeft(settingsLeft + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); +} + +void EmojiPan::paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const { + if (auto unread = Global::FeaturedStickerSetsUnreadCount()) { + Dialogs::Layout::UnreadBadgeStyle unreadSt; + unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersPanel; + unreadSt.size = st::stickersSettingsUnreadSize; + int unreadRight = iconLeft + st::rbEmoji.width - st::stickersSettingsUnreadPosition.x(); + if (rtl()) unreadRight = width() - unreadRight; + int unreadTop = _iconsTop + st::stickersSettingsUnreadPosition.y(); + Dialogs::Layout::paintUnreadCount(p, QString::number(unread), unreadRight, unreadTop, unreadSt); + } +} + +void EmojiPan::paintEvent(QPaintEvent *e) { + Painter p(this); + + float64 o = 1; + if (!_cache.isNull()) { + p.setOpacity(o = a_opacity.current()); + } + + QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); + + _shadow.paint(p, r, st::dropdownDef.shadowShift); + + if (_toCache.isNull()) { + if (_cache.isNull()) { + p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::white->b); + if (_stickersShown && s_inner.showSectionIcons()) { + p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories); + paintStickerSettingsIcon(p); + + if (!_icons.isEmpty()) { + int x = _iconsLeft, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); + + QRect clip(x, _iconsTop, _iconsLeft + 7 * st::rbEmoji.width - x, st::rbEmoji.height); + if (rtl()) clip.moveLeft(width() - x - clip.width()); + p.setClipRect(clip); + + auto getSpecialSetIcon = [](uint64 setId, bool active) { + if (setId == Stickers::NoneSetId) { + return active ? st::savedGifsActive : st::savedGifsOver; + } else if (setId == Stickers::FeaturedSetId) { + return active ? st::featuredStickersActive : st::featuredStickersOver; + } + return active ? st::rbEmojiRecent.chkImageRect : st::rbEmojiRecent.imageRect; + }; + + int i = 0; + i += _iconsX.current() / int(st::rbEmoji.width); + x -= _iconsX.current() % int(st::rbEmoji.width); + selxrel -= _iconsX.current(); + for (int l = qMin(_icons.size(), i + 8); i < l; ++i) { + auto &s = _icons.at(i); + if (s.sticker) { + s.sticker->thumb->load(); + QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh)); + + p.drawPixmapLeft(x + (st::rbEmoji.width - s.pixw) / 2, _iconsTop + (st::rbEmoji.height - s.pixh) / 2, width(), pix); + x += st::rbEmoji.width; + } else { + if (selxrel != x) { + p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), getSpecialSetIcon(s.setId, false)); + } + if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) { + p.setOpacity(1 - (qAbs(selxrel - x) / float64(st::rbEmoji.width))); + p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), getSpecialSetIcon(s.setId, true)); + p.setOpacity(1); + } + if (s.setId == Stickers::FeaturedSetId) { + paintFeaturedStickerSetsBadge(p, x); + } + x += st::rbEmoji.width; + } + } + + if (rtl()) selx = width() - selx - st::rbEmoji.width; + p.setOpacity(1.); + p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor); + + float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.); + if (o_left > 0) { + p.setOpacity(o_left); + p.drawSpriteLeft(QRect(_iconsLeft, _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft); + } + float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.); + if (o_right > 0) { + p.setOpacity(o_right); + p.drawSpriteRight(QRect(width() - _iconsLeft - 7 * st::rbEmoji.width, _iconsTop, st::stickerIconRight.pxWidth(), st::rbEmoji.height), width(), st::stickerIconRight); + } + } + } else if (_stickersShown) { + int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); + p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::white); + } else { + p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), st::rbEmoji.height, st::emojiPanCategories); + int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width()); + p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::emojiPanCategories); + } + } else { + p.fillRect(r, st::white); + p.drawPixmap(r.left(), r.top(), _cache); + } + } else { + p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::rbEmoji.height), st::white->b); + p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height), st::emojiPanCategories->b); + p.setOpacity(o * a_fromAlpha.current()); + QRect fromDst = QRect(r.left() + a_fromCoord.current(), r.top(), _fromCache.width() / cIntRetinaFactor(), _fromCache.height() / cIntRetinaFactor()); + QRect fromSrc = QRect(0, 0, _fromCache.width(), _fromCache.height()); + if (fromDst.x() < r.left() + r.width() && fromDst.x() + fromDst.width() > r.left()) { + if (fromDst.x() < r.left()) { + fromSrc.setX((r.left() - fromDst.x()) * cIntRetinaFactor()); + fromDst.setX(r.left()); + } else if (fromDst.x() + fromDst.width() > r.left() + r.width()) { + fromSrc.setWidth((r.left() + r.width() - fromDst.x()) * cIntRetinaFactor()); + fromDst.setWidth(r.left() + r.width() - fromDst.x()); + } + p.drawPixmap(fromDst, _fromCache, fromSrc); + } + p.setOpacity(o * a_toAlpha.current()); + QRect toDst = QRect(r.left() + a_toCoord.current(), r.top(), _toCache.width() / cIntRetinaFactor(), _toCache.height() / cIntRetinaFactor()); + QRect toSrc = QRect(0, 0, _toCache.width(), _toCache.height()); + if (toDst.x() < r.left() + r.width() && toDst.x() + toDst.width() > r.left()) { + if (toDst.x() < r.left()) { + toSrc.setX((r.left() - toDst.x()) * cIntRetinaFactor()); + toDst.setX(r.left()); + } else if (toDst.x() + toDst.width() > r.left() + r.width()) { + toSrc.setWidth((r.left() + r.width() - toDst.x()) * cIntRetinaFactor()); + toDst.setWidth(r.left() + r.width() - toDst.x()); + } + p.drawPixmap(toDst, _toCache, toSrc); + } + } +} + +void EmojiPan::moveBottom(int32 bottom, bool force) { + _bottom = bottom; + if (isHidden() && !force) { + move(x(), _bottom - height()); + return; + } + if (_stickersShown && s_inner.inlineResultsShown()) { + moveToLeft(0, _bottom - height()); + } else { + moveToRight(0, _bottom - height()); + } +} + +void EmojiPan::enterEvent(QEvent *e) { + _hideTimer.stop(); + if (_hiding) showStart(); +} + +bool EmojiPan::preventAutoHide() const { + return _removingSetId || _displayingSetId; +} + +void EmojiPan::leaveEvent(QEvent *e) { + if (preventAutoHide() || s_inner.inlineResultsShown()) return; + if (_a_appearance.animating()) { + hideStart(); + } else { + _hideTimer.start(300); + } +} + +void EmojiPan::otherEnter() { + _hideTimer.stop(); + showStart(); +} + +void EmojiPan::otherLeave() { + if (preventAutoHide() || s_inner.inlineResultsShown()) return; + if (_a_appearance.animating()) { + hideStart(); + } else { + _hideTimer.start(0); + } +} + +void EmojiPan::mousePressEvent(QMouseEvent *e) { + if (!_stickersShown) return; + _iconsMousePos = e ? e->globalPos() : QCursor::pos(); + updateSelected(); + + if (_iconOver == _icons.size()) { + Ui::showLayer(new StickersBox()); + } else { + _iconDown = _iconOver; + _iconsMouseDown = _iconsMousePos; + _iconsStartX = _iconsX.current(); + } +} + +void EmojiPan::mouseMoveEvent(QMouseEvent *e) { + if (!_stickersShown) return; + _iconsMousePos = e ? e->globalPos() : QCursor::pos(); + updateSelected(); + + if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= 0) { + if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) { + _iconsDragging = true; + } + } + if (_iconsDragging) { + int32 newX = snap(_iconsStartX + (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()), 0, _iconsMax); + if (newX != _iconsX.current()) { + _iconsX = anim::ivalue(newX, newX); + _iconsStartAnim = 0; + if (_iconAnimations.isEmpty()) _a_icons.stop(); + updateIcons(); + } + } +} + +void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { + if (!_stickersShown || _icons.isEmpty()) return; + + int32 wasDown = _iconDown; + _iconDown = -1; + + _iconsMousePos = e ? e->globalPos() : QCursor::pos(); + if (_iconsDragging) { + int32 newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax); + if (newX != _iconsX.current()) { + _iconsX = anim::ivalue(newX, newX); + _iconsStartAnim = 0; + if (_iconAnimations.isEmpty()) _a_icons.stop(); + updateIcons(); + } + _iconsDragging = false; + updateSelected(); + } else { + updateSelected(); + + if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) { + _iconSelX = anim::ivalue(_iconOver * st::rbEmoji.width, _iconOver * st::rbEmoji.width); + s_inner.showStickerSet(_icons.at(_iconOver).setId); + } + } +} + +bool EmojiPan::event(QEvent *e) { + if (e->type() == QEvent::TouchBegin) { + + } else if (e->type() == QEvent::Wheel) { + if (!_icons.isEmpty() && _iconOver >= 0 && _iconOver < _icons.size() && _iconDown < 0) { + QWheelEvent *ev = static_cast(e); + bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal); + bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical); + if (hor) _horizontal = true; + int32 newX = _iconsX.current(); + if (/*_horizontal && */hor) { + newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax); + } else if (/*!_horizontal && */ver) { + newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax); + } + if (newX != _iconsX.current()) { + _iconsX = anim::ivalue(newX, newX); + _iconsStartAnim = 0; + if (_iconAnimations.isEmpty()) _a_icons.stop(); + updateSelected(); + updateIcons(); + } + } + } + return TWidget::event(e); +} + +void EmojiPan::fastHide() { + if (_a_appearance.animating()) { + _a_appearance.stop(); + } + a_opacity = anim::fvalue(0, 0); + _hideTimer.stop(); + hide(); + _cache = QPixmap(); +} + +void EmojiPan::refreshStickers() { + s_inner.refreshStickers(); + if (!_stickersShown) { + s_inner.preloadImages(); + } + update(); +} + +void EmojiPan::refreshSavedGifs() { + e_switch.updateText(); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + s_inner.refreshSavedGifs(); + if (!_stickersShown) { + s_inner.preloadImages(); + } +} + +void EmojiPan::onRefreshIcons() { + _iconOver = -1; + _iconHovers.clear(); + _iconAnimations.clear(); + s_inner.fillIcons(_icons); + s_inner.fillPanels(s_panels); + _iconsX.finish(); + _iconSelX.finish(); + _iconsStartAnim = 0; + _a_icons.stop(); + if (_icons.isEmpty()) { + _iconsMax = 0; + } else { + _iconHovers = QVector(_icons.size(), 0); + _iconsMax = qMax(int((_icons.size() - 7) * st::rbEmoji.width), 0); + } + if (_iconsX.current() > _iconsMax) { + _iconsX = anim::ivalue(_iconsMax, _iconsMax); + } + updatePanelsPositions(s_panels, s_scroll.scrollTop()); + updateSelected(); + if (_stickersShown) { + validateSelectedIcon(); + updateContentHeight(); + } + updateIcons(); +} + +void EmojiPan::onRefreshPanels() { + s_inner.refreshPanels(s_panels); + e_inner.refreshPanels(e_panels); + if (_stickersShown) { + updatePanelsPositions(s_panels, s_scroll.scrollTop()); + } else { + updatePanelsPositions(e_panels, e_scroll.scrollTop()); + } +} + +void EmojiPan::leaveToChildEvent(QEvent *e, QWidget *child) { + if (!_stickersShown) return; + _iconsMousePos = QCursor::pos(); + updateSelected(); +} + +void EmojiPan::updateSelected() { + if (_iconDown >= 0) { + return; + } + + QPoint p(mapFromGlobal(_iconsMousePos)); + int32 x = p.x(), y = p.y(), newOver = -1; + if (rtl()) x = width() - x; + x -= _iconsLeft; + if (x >= st::rbEmoji.width * 7 && x < st::rbEmoji.width * 8 && y >= _iconsTop && y < _iconsTop + st::rbEmoji.height) { + newOver = _icons.size(); + } else if (!_icons.isEmpty()) { + if (y >= _iconsTop && y < _iconsTop + st::rbEmoji.height && x >= 0 && x < 7 * st::rbEmoji.width && x < _icons.size() * st::rbEmoji.width) { + x += _iconsX.current(); + newOver = qFloor(x / st::rbEmoji.width); + } + } + if (newOver != _iconOver) { + if (newOver < 0) { + setCursor(style::cur_default); + } else if (_iconOver < 0) { + setCursor(style::cur_pointer); + } + bool startanim = false; + if (_iconOver >= 0 && _iconOver < _icons.size()) { + _iconAnimations.remove(_iconOver + 1); + if (_iconAnimations.find(-_iconOver - 1) == _iconAnimations.end()) { + if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; + _iconAnimations.insert(-_iconOver - 1, getms()); + } + } + _iconOver = newOver; + if (_iconOver >= 0 && _iconOver < _icons.size()) { + _iconAnimations.remove(-_iconOver - 1); + if (_iconAnimations.find(_iconOver + 1) == _iconAnimations.end()) { + if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true; + _iconAnimations.insert(_iconOver + 1, getms()); + } + } + if (startanim && !_a_icons.animating()) _a_icons.start(); + } +} + +void EmojiPan::updateIcons() { + if (!_stickersShown || !s_inner.showSectionIcons()) return; + + QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); + update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); +} + +void EmojiPan::step_icons(uint64 ms, bool timer) { + if (!_stickersShown) { + _a_icons.stop(); + return; + } + + for (Animations::iterator i = _iconAnimations.begin(); i != _iconAnimations.end();) { + int index = qAbs(i.key()) - 1; + float64 dt = float64(ms - i.value()) / st::emojiPanDuration; + if (index >= _iconHovers.size()) { + i = _iconAnimations.erase(i); + } else if (dt >= 1) { + _iconHovers[index] = (i.key() > 0) ? 1 : 0; + i = _iconAnimations.erase(i); + } else { + _iconHovers[index] = (i.key() > 0) ? dt : (1 - dt); + ++i; + } + } + + if (_iconsStartAnim) { + float64 dt = (ms - _iconsStartAnim) / float64(st::stickerIconMove); + if (dt >= 1) { + _iconsStartAnim = 0; + _iconsX.finish(); + _iconSelX.finish(); + } else { + _iconsX.update(dt, anim::linear); + _iconSelX.update(dt, anim::linear); + } + if (timer) updateSelected(); + } + + if (timer) updateIcons(); + + if (_iconAnimations.isEmpty() && !_iconsStartAnim) { + _a_icons.stop(); + } +} + +void EmojiPan::step_slide(float64 ms, bool timer) { + float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration; + float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0; + if (dt2 >= 1) { + _a_slide.stop(); + a_fromCoord.finish(); + a_fromAlpha.finish(); + a_toCoord.finish(); + a_toAlpha.finish(); + _fromCache = _toCache = QPixmap(); + if (_cache.isNull()) showAll(); + } else { + a_fromCoord.update(dt1, st::introHideFunc); + a_fromAlpha.update(dt1, st::introAlphaHideFunc); + a_toCoord.update(dt2, st::introShowFunc); + a_toAlpha.update(dt2, st::introAlphaShowFunc); + } + if (timer) update(); +} + +void EmojiPan::step_appearance(float64 ms, bool timer) { + if (_cache.isNull()) { + _a_appearance.stop(); + return; + } + + float64 dt = ms / st::dropdownDef.duration; + if (dt >= 1) { + _a_appearance.stop(); + a_opacity.finish(); + if (_hiding) { + hideFinish(); + } else { + _cache = QPixmap(); + if (_toCache.isNull()) showAll(); + } + } else { + a_opacity.update(dt, anim::linear); + } + if (timer) update(); +} + +void EmojiPan::hideStart() { + if (preventAutoHide() || s_inner.inlineResultsShown()) return; + + hideAnimated(); +} + +void EmojiPan::prepareShowHideCache() { + if (_cache.isNull()) { + QPixmap from = _fromCache, to = _toCache; + _fromCache = _toCache = QPixmap(); + showAll(); + _cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _fromCache = from; _toCache = to; + } +} + +void EmojiPan::hideAnimated() { + if (_hiding) return; + + prepareShowHideCache(); + hideAll(); + _hiding = true; + a_opacity.start(0); + _a_appearance.start(); +} + +void EmojiPan::hideFinish() { + hide(); + e_inner.hideFinish(); + s_inner.hideFinish(true); + _cache = _toCache = _fromCache = QPixmap(); + _a_slide.stop(); + _horizontal = false; + _hiding = false; + + e_scroll.scrollToY(0); + if (!_recent.checked()) { + _noTabUpdate = true; + _recent.setChecked(true); + _noTabUpdate = false; + } + s_scroll.scrollToY(0); + _iconOver = _iconDown = -1; + _iconSel = 0; + _iconsX = anim::ivalue(0, 0); + _iconSelX = anim::ivalue(0, 0); + _iconsStartAnim = 0; + _a_icons.stop(); + _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); + _iconAnimations.clear(); + + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); +} + +void EmojiPan::showStart() { + if (!isHidden() && !_hiding) { + return; + } + if (isHidden()) { + e_inner.refreshRecent(); + if (s_inner.inlineResultsShown() && refreshInlineRows()) { + _stickersShown = true; + _shownFromInlineQuery = true; + } else { + s_inner.refreshRecent(); + _stickersShown = false; + _shownFromInlineQuery = false; + _cache = QPixmap(); // clear after refreshInlineRows() + } + recountContentMaxHeight(); + s_inner.preloadImages(); + _fromCache = _toCache = QPixmap(); + _a_slide.stop(); + moveBottom(y() + height(), true); + } else if (_hiding) { + if (s_inner.inlineResultsShown() && refreshInlineRows()) { + onSwitch(); + } + } + prepareShowHideCache(); + hideAll(); + _hiding = false; + show(); + a_opacity.start(1); + _a_appearance.start(); + emit updateStickers(); +} + +bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { + if (e->type() == QEvent::Enter) { + //if (dynamic_cast(obj)) { + // enterEvent(e); + //} else { + otherEnter(); + //} + } else if (e->type() == QEvent::Leave) { + //if (dynamic_cast(obj)) { + // leaveEvent(e); + //} else { + otherLeave(); + //} + } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { + if (isHidden() || _hiding) { + _hideTimer.stop(); + showStart(); + } else { + hideAnimated(); + } + } + return false; +} + +void EmojiPan::stickersInstalled(uint64 setId) { + _stickersShown = true; + if (isHidden()) { + moveBottom(y() + height(), true); + show(); + a_opacity = anim::fvalue(0, 1); + a_opacity.update(0, anim::linear); + _cache = _fromCache = _toCache = QPixmap(); + } + showAll(); + s_inner.showStickerSet(setId); + updateContentHeight(); + showStart(); +} + +void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { + if (_stickersShown && !isHidden()) { + s_inner.notify_inlineItemLayoutChanged(layout); + } +} + +void EmojiPan::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { + if (_stickersShown && !isHidden()) { + s_inner.ui_repaintInlineItem(layout); + } +} + +bool EmojiPan::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { + if (_stickersShown && !isHidden()) { + return s_inner.ui_isInlineItemVisible(layout); + } + return false; +} + +bool EmojiPan::ui_isInlineItemBeingChosen() { + if (_stickersShown && !isHidden()) { + return s_inner.ui_isInlineItemBeingChosen(); + } + return false; +} + +void EmojiPan::showAll() { + if (_stickersShown) { + s_scroll.show(); + _recent.hide(); + _people.hide(); + _nature.hide(); + _food.hide(); + _activity.hide(); + _travel.hide(); + _objects.hide(); + _symbols.hide(); + e_scroll.hide(); + } else { + s_scroll.hide(); + _recent.show(); + _people.show(); + _nature.show(); + _food.show(); + _activity.show(); + _travel.show(); + _objects.show(); + _symbols.show(); + e_scroll.show(); + } +} + +void EmojiPan::hideAll() { + _recent.hide(); + _people.hide(); + _nature.hide(); + _food.hide(); + _activity.hide(); + _travel.hide(); + _objects.hide(); + _symbols.hide(); + e_scroll.hide(); + s_scroll.hide(); + e_inner.clearSelection(true); + s_inner.clearSelection(true); +} + +void EmojiPan::onTabChange() { + if (_noTabUpdate) return; + DBIEmojiTab newTab = dbietRecent; + if (_people.checked()) newTab = dbietPeople; + else if (_nature.checked()) newTab = dbietNature; + else if (_food.checked()) newTab = dbietFood; + else if (_activity.checked()) newTab = dbietActivity; + else if (_travel.checked()) newTab = dbietTravel; + else if (_objects.checked()) newTab = dbietObjects; + else if (_symbols.checked()) newTab = dbietSymbols; + e_inner.showEmojiPack(newTab); +} + +void EmojiPan::updatePanelsPositions(const QVector &panels, int32 st) { + for (int32 i = 0, l = panels.size(); i < l; ++i) { + int32 y = panels.at(i)->wantedY() - st; + if (y < 0) { + y = (i + 1 < l) ? qMin(panels.at(i + 1)->wantedY() - st - int(st::emojiPanHeader), 0) : 0; + } + panels.at(i)->move(0, y); + panels.at(i)->setDeleteVisible(y >= st::emojiPanHeader); + + // Somehow the panels gets hidden (not displayed) when scrolling + // by clicking on the scroll bar to the middle of the panel. + // This bug occurs only in the Section::Featured stickers. + if (s_inner.currentSet(0) == Stickers::FeaturedSetId) { + panels.at(i)->repaint(); + } + } +} + +void EmojiPan::onScrollEmoji() { + auto st = e_scroll.scrollTop(); + + updatePanelsPositions(e_panels, st); + + auto tab = e_inner.currentTab(st); + FlatRadiobutton *check = nullptr; + switch (tab) { + case dbietRecent: check = &_recent; break; + case dbietPeople: check = &_people; break; + case dbietNature: check = &_nature; break; + case dbietFood: check = &_food; break; + case dbietActivity: check = &_activity; break; + case dbietTravel: check = &_travel; break; + case dbietObjects: check = &_objects; break; + case dbietSymbols: check = &_symbols; break; + } + if (check && !check->checked()) { + _noTabUpdate = true; + check->setChecked(true); + _noTabUpdate = false; + } + + e_inner.setScrollTop(st); +} + +void EmojiPan::onScrollStickers() { + auto st = s_scroll.scrollTop(); + + updatePanelsPositions(s_panels, st); + + validateSelectedIcon(true); + if (st + s_scroll.height() > s_scroll.scrollTopMax()) { + onInlineRequest(); + } + + s_inner.setScrollTop(st); +} + +void EmojiPan::validateSelectedIcon(bool animated) { + uint64 setId = s_inner.currentSet(s_scroll.scrollTop()); + int32 newSel = 0; + for (int i = 0, l = _icons.size(); i < l; ++i) { + if (_icons.at(i).setId == setId) { + newSel = i; + break; + } + } + if (newSel != _iconSel) { + _iconSel = newSel; + if (animated) { + _iconSelX.start(newSel * st::rbEmoji.width); + } else { + _iconSelX = anim::ivalue(newSel * st::rbEmoji.width, newSel * st::rbEmoji.width); + } + _iconsX.start(snap((2 * newSel - 7) * int(st::rbEmoji.width) / 2, 0, _iconsMax)); + _iconsStartAnim = getms(); + _a_icons.start(); + updateSelected(); + updateIcons(); + } +} + +void EmojiPan::onSwitch() { + QPixmap cache = _cache; + _fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _stickersShown = !_stickersShown; + if (!_stickersShown) { + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); + } else { + if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { + s_inner.showStickerSet(Stickers::DefaultSetId); + } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && Global::StickerSetsOrder().isEmpty()) { + s_inner.showStickerSet(Stickers::NoneSetId); + } else { + s_inner.updateShowingSavedGifs(); + } + if (cShowingSavedGifs()) { + s_inner.showFinish(); + } + validateSelectedIcon(); + updateContentHeight(); + } + _iconOver = -1; + _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); + _iconAnimations.clear(); + _a_icons.stop(); + + _cache = QPixmap(); + showAll(); + _toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _cache = cache; + + hideAll(); + + if (_stickersShown) { + e_inner.hideFinish(); + } else { + s_inner.hideFinish(false); + } + + a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanWidth, 0) : anim::ivalue(-st::emojiPanWidth, 0); + a_toAlpha = anim::fvalue(0, 1); + a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanWidth) : anim::ivalue(0, st::emojiPanWidth); + a_fromAlpha = anim::fvalue(1, 0); + + _a_slide.start(); + update(); +} + +void EmojiPan::onDisplaySet(quint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it != sets.cend()) { + _displayingSetId = setId; + auto box = new StickerSetBox(Stickers::inputSetId(*it)); + connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide())); + Ui::showLayer(box, KeepOtherLayers); + } +} + +void EmojiPan::onInstallSet(quint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it != sets.cend()) { + MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_bool(false)), rpcDone(&EmojiPan::installSetDone), rpcFail(&EmojiPan::installSetFail, setId)); + + Stickers::installLocally(setId); + } +} + +void EmojiPan::installSetDone(const MTPmessages_StickerSetInstallResult &result) { + if (result.type() == mtpc_messages_stickerSetInstallResultArchive) { + Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive()); + } +} + +bool EmojiPan::installSetFail(uint64 setId, const RPCError &error) { + if (MTP::isDefaultHandledError(error)) { + return false; + } + Stickers::undoInstallLocally(setId); + return true; +} + +void EmojiPan::onRemoveSet(quint64 setId) { + auto &sets = Global::StickerSets(); + auto it = sets.constFind(setId); + if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { + _removingSetId = it->id; + ConfirmBox *box = new ConfirmBox(lng_stickers_remove_pack(lt_sticker_pack, it->title), lang(lng_box_remove)); + connect(box, SIGNAL(confirmed()), this, SLOT(onRemoveSetSure())); + connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide())); + Ui::showLayer(box); + } +} + +void EmojiPan::onRemoveSetSure() { + Ui::hideLayer(); + auto &sets = Global::RefStickerSets(); + auto it = sets.find(_removingSetId); + if (it != sets.cend() && !(it->flags & MTPDstickerSet::Flag::f_official)) { + if (it->id && it->access) { + MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)))); + } else if (!it->shortName.isEmpty()) { + MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetShortName(MTP_string(it->shortName)))); + } + bool writeRecent = false; + RecentStickerPack &recent(cGetRecentStickers()); + for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { + if (it->stickers.indexOf(i->first) >= 0) { + i = recent.erase(i); + writeRecent = true; + } else { + ++i; + } + } + it->flags &= ~MTPDstickerSet::Flag::f_installed; + if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured) && !(it->flags & MTPDstickerSet_ClientFlag::f_special)) { + sets.erase(it); + } + int removeIndex = Global::StickerSetsOrder().indexOf(_removingSetId); + if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex); + refreshStickers(); + Local::writeInstalledStickers(); + if (writeRecent) Local::writeUserSettings(); + } + _removingSetId = 0; +} + +void EmojiPan::onDelayedHide() { + if (!rect().contains(mapFromGlobal(QCursor::pos()))) { + _hideTimer.start(3000); + } + _removingSetId = 0; + _displayingSetId = 0; +} + +void EmojiPan::clearInlineBot() { + inlineBotChanged(); + e_switch.updateText(); + e_switch.moveToRight(0, 0, st::emojiPanWidth); +} + +bool EmojiPan::hideOnNoInlineResults() { + return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); +} + +void EmojiPan::inlineBotChanged() { + if (!_inlineBot) return; + + if (!isHidden() && !_hiding) { + if (hideOnNoInlineResults() || !rect().contains(mapFromGlobal(QCursor::pos()))) { + hideAnimated(); + } + } + + if (_inlineRequestId) MTP::cancel(_inlineRequestId); + _inlineRequestId = 0; + _inlineQuery = _inlineNextQuery = _inlineNextOffset = QString(); + _inlineBot = nullptr; + for (InlineCache::const_iterator i = _inlineCache.cbegin(), e = _inlineCache.cend(); i != e; ++i) { + delete i.value(); + } + _inlineCache.clear(); + s_inner.inlineBotChanged(); + s_inner.hideInlineRowsPanel(); + + Notify::inlineBotRequesting(false); +} + +void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) { + _inlineRequestId = 0; + Notify::inlineBotRequesting(false); + + auto it = _inlineCache.find(_inlineQuery); + + bool adding = (it != _inlineCache.cend()); + if (result.type() == mtpc_messages_botResults) { + const auto &d(result.c_messages_botResults()); + const auto &v(d.vresults.c_vector().v); + uint64 queryId(d.vquery_id.v); + + if (!adding) { + it = _inlineCache.insert(_inlineQuery, new internal::InlineCacheEntry()); + } + it.value()->nextOffset = qs(d.vnext_offset); + if (d.has_switch_pm() && d.vswitch_pm.type() == mtpc_inlineBotSwitchPM) { + const auto &switchPm = d.vswitch_pm.c_inlineBotSwitchPM(); + it.value()->switchPmText = qs(switchPm.vtext); + it.value()->switchPmStartToken = qs(switchPm.vstart_param); + } + + if (int count = v.size()) { + it.value()->results.reserve(it.value()->results.size() + count); + } + int added = 0; + for_const (const auto &res, v) { + if (auto result = InlineBots::Result::create(queryId, res)) { + ++added; + it.value()->results.push_back(result.release()); + } + } + + if (!added) { + it.value()->nextOffset = QString(); + } + } else if (adding) { + it.value()->nextOffset = QString(); + } + + if (!showInlineRows(!adding)) { + it.value()->nextOffset = QString(); + } + onScrollStickers(); +} + +bool EmojiPan::inlineResultsFail(const RPCError &error) { + // show error? + Notify::inlineBotRequesting(false); + _inlineRequestId = 0; + return true; +} + +void EmojiPan::queryInlineBot(UserData *bot, PeerData *peer, QString query) { + bool force = false; + _inlineQueryPeer = peer; + if (bot != _inlineBot) { + inlineBotChanged(); + _inlineBot = bot; + force = true; + //if (_inlineBot->isBotInlineGeo()) { + // Ui::showLayer(new InformBox(lang(lng_bot_inline_geo_unavailable))); + //} + } + //if (_inlineBot && _inlineBot->isBotInlineGeo()) { + // return; + //} + + if (_inlineQuery != query || force) { + if (_inlineRequestId) { + MTP::cancel(_inlineRequestId); + _inlineRequestId = 0; + Notify::inlineBotRequesting(false); + } + if (_inlineCache.contains(query)) { + _inlineRequestTimer.stop(); + _inlineQuery = _inlineNextQuery = query; + showInlineRows(true); + } else { + _inlineNextQuery = query; + _inlineRequestTimer.start(InlineBotRequestDelay); + } + } +} + +void EmojiPan::onInlineRequest() { + if (_inlineRequestId || !_inlineBot || !_inlineQueryPeer) return; + _inlineQuery = _inlineNextQuery; + + QString nextOffset; + InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); + if (i != _inlineCache.cend()) { + nextOffset = i.value()->nextOffset; + if (nextOffset.isEmpty()) return; + } + Notify::inlineBotRequesting(true); + MTPmessages_GetInlineBotResults::Flags flags = 0; + _inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(MTP_flags(flags), _inlineBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); +} + +void EmojiPan::onEmptyInlineRows() { + if (_shownFromInlineQuery || hideOnNoInlineResults()) { + hideAnimated(); + s_inner.clearInlineRowsPanel(); + } else if (!_inlineBot) { + s_inner.hideInlineRowsPanel(); + } else { + s_inner.clearInlineRowsPanel(); + } +} + +bool EmojiPan::refreshInlineRows(int32 *added) { + auto i = _inlineCache.constFind(_inlineQuery); + const internal::InlineCacheEntry *entry = nullptr; + if (i != _inlineCache.cend()) { + if (!i.value()->results.isEmpty() || !i.value()->switchPmText.isEmpty()) { + entry = i.value(); + } + _inlineNextOffset = i.value()->nextOffset; + } + if (!entry) prepareShowHideCache(); + int32 result = s_inner.refreshInlineRows(_inlineBot, entry, false); + if (added) *added = result; + return (entry != nullptr); +} + +int32 EmojiPan::showInlineRows(bool newResults) { + int32 added = 0; + bool clear = !refreshInlineRows(&added); + if (newResults) s_scroll.scrollToY(0); + + e_switch.updateText(s_inner.inlineResultsShown() ? _inlineBot->username : QString()); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + + bool hidden = isHidden(); + if (!hidden && !clear) { + recountContentMaxHeight(); + } + if (clear) { + if (!hidden && hideOnNoInlineResults()) { + hideAnimated(); + } else if (!_hiding) { + _cache = QPixmap(); // clear after refreshInlineRows() + } + } else { + _hideTimer.stop(); + if (hidden || _hiding) { + showStart(); + } else if (!_stickersShown) { + onSwitch(); + } + } + + return added; +} + +void EmojiPan::recountContentMaxHeight() { + if (_shownFromInlineQuery) { + _contentMaxHeight = qMin(s_inner.countHeight(true), int(st::emojiPanMaxHeight)); + } else { + _contentMaxHeight = st::emojiPanMaxHeight; + } + updateContentHeight(); +} diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h new file mode 100644 index 000000000..ff6d609b5 --- /dev/null +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -0,0 +1,657 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "ui/twidget.h" +#include "ui/boxshadow.h" + +namespace InlineBots { +namespace Layout { +class ItemBase; +} // namespace Layout +class Result; +} // namespace InlineBots + +namespace internal { + +constexpr int InlineItemsMaxPerRow = 5; +constexpr int EmojiColorsCount = 5; + +using InlineResult = InlineBots::Result; +using InlineResults = QList; +using InlineItem = InlineBots::Layout::ItemBase; + +struct InlineCacheEntry { + ~InlineCacheEntry() { + clearResults(); + } + QString nextOffset; + QString switchPmText, switchPmStartToken; + InlineResults results; // owns this results list + void clearResults(); +}; + +class EmojiColorPicker : public TWidget { + Q_OBJECT + +public: + + EmojiColorPicker(); + + void showEmoji(uint32 code); + + void paintEvent(QPaintEvent *e); + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + + void step_appearance(float64 ms, bool timer); + void step_selected(uint64 ms, bool timer); + void showStart(); + + void clearSelection(bool fast = false); + +public slots: + + void hideStart(bool fast = false); + +signals: + + void emojiSelected(EmojiPtr emoji); + void hidden(); + +private: + + void drawVariant(Painter &p, int variant); + + void updateSelected(); + + bool _ignoreShow = false; + + EmojiPtr _variants[EmojiColorsCount + 1]; + + typedef QMap EmojiAnimations; // index - showing, -index - hiding + EmojiAnimations _emojiAnimations; + Animation _a_selected; + + float64 _hovers[EmojiColorsCount + 1]; + + int _selected = -1; + int _pressedSel = -1; + QPoint _lastMousePos; + + bool _hiding = false; + QPixmap _cache; + + anim::fvalue a_opacity; + Animation _a_appearance; + + QTimer _hideTimer; + + BoxShadow _shadow; + +}; + +class EmojiPanel; +class EmojiPanInner : public TWidget { + Q_OBJECT + +public: + EmojiPanInner(); + + void setMaxHeight(int32 h); + void paintEvent(QPaintEvent *e) override; + + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; + void enterFromChildEvent(QEvent *e, QWidget *child) override; + + void step_selected(uint64 ms, bool timer); + void hideFinish(); + + void showEmojiPack(DBIEmojiTab packIndex); + + void clearSelection(bool fast = false); + + DBIEmojiTab currentTab(int yOffset) const; + + void refreshRecent(); + + void setScrollTop(int top); + + void fillPanels(QVector &panels); + void refreshPanels(QVector &panels); + +public slots: + + void updateSelected(); + + void onShowPicker(); + void onPickerHidden(); + void onColorSelected(EmojiPtr emoji); + + bool checkPickerHide(); + +signals: + + void selected(EmojiPtr emoji); + + void switchToStickers(); + + void scrollToY(int y); + void disableScroll(bool dis); + + void needRefreshPanels(); + void saveConfigDelayed(int32 delay); + +private: + + int32 _maxHeight; + + int countHeight(); + void selectEmoji(EmojiPtr emoji); + + QRect emojiRect(int tab, int sel); + + typedef QMap Animations; // index - showing, -index - hiding + Animations _animations; + Animation _a_selected; + + int _top = 0, _counts[emojiTabCount]; + + QVector _emojis[emojiTabCount]; + QVector _hovers[emojiTabCount]; + + int32 _esize; + + int _selected = -1; + int _pressedSel = -1; + int _pickerSel = -1; + QPoint _lastMousePos; + + EmojiColorPicker _picker; + QTimer _showPickerTimer; +}; + +struct StickerIcon { + StickerIcon(uint64 setId) : setId(setId) { + } + StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) { + } + uint64 setId; + DocumentData *sticker = nullptr; + int pixw = 0; + int pixh = 0; +}; + +class StickerPanInner : public TWidget { + Q_OBJECT + +public: + + StickerPanInner(); + + void setMaxHeight(int32 h); + void paintEvent(QPaintEvent *e) override; + + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; + void enterFromChildEvent(QEvent *e, QWidget *child) override; + + void step_selected(uint64 ms, bool timer); + + void hideFinish(bool completely); + void showFinish(); + void showStickerSet(uint64 setId); + void updateShowingSavedGifs(); + + bool showSectionIcons() const; + void clearSelection(bool fast = false); + + void refreshStickers(); + void refreshRecentStickers(bool resize = true); + void refreshSavedGifs(); + int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted); + void refreshRecent(); + void inlineBotChanged(); + void hideInlineRowsPanel(); + void clearInlineRowsPanel(); + + void fillIcons(QList &icons); + void fillPanels(QVector &panels); + void refreshPanels(QVector &panels); + + void setScrollTop(int top); + void preloadImages(); + + uint64 currentSet(int yOffset) const; + + void notify_inlineItemLayoutChanged(const InlineItem *layout); + void ui_repaintInlineItem(const InlineItem *layout); + bool ui_isInlineItemVisible(const InlineItem *layout); + bool ui_isInlineItemBeingChosen(); + + bool inlineResultsShown() const { + return (_section == Section::Inlines); + } + int countHeight(bool plain = false); + + ~StickerPanInner(); + +private slots: + void updateSelected(); + void onSettings(); + void onPreview(); + void onUpdateInlineItems(); + void onSwitchPm(); + +signals: + void selected(DocumentData *sticker); + void selected(PhotoData *photo); + void selected(InlineBots::Result *result, UserData *bot); + + void displaySet(quint64 setId); + void installSet(quint64 setId); + void removeSet(quint64 setId); + + void refreshIcons(); + void emptyInlineRows(); + + void switchToEmoji(); + + void scrollToY(int y); + void scrollUpdated(); + void disableScroll(bool dis); + void needRefreshPanels(); + + void saveConfigDelayed(int32 delay); + +private: + struct Set { + Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) { + } + uint64 id; + MTPDstickerSet::Flags flags; + QString title; + QVector hovers; + StickerPack pack; + }; + using Sets = QList; + Sets &shownSets() { + return (_section == Section::Featured) ? _featuredSets : _mySets; + } + const Sets &shownSets() const { + return const_cast(this)->shownSets(); + } + int featuredRowHeight() const; + + bool showingInlineItems() const { // Gifs or Inline results + return (_section == Section::Inlines) || (_section == Section::Gifs); + } + + void paintInlineItems(Painter &p, const QRect &r); + void paintStickers(Painter &p, const QRect &r); + void paintSticker(Painter &p, Set &set, int y, int index); + bool featuredHasAddButton(int index) const; + int featuredContentWidth() const; + QRect featuredAddRect(int y) const; + + void refreshSwitchPmButton(const InlineCacheEntry *entry); + + void appendSet(Sets &to, uint64 setId); + + void selectEmoji(EmojiPtr emoji); + QRect stickerRect(int tab, int sel); + + int32 _maxHeight; + + typedef QMap Animations; // index - showing, -index - hiding + Animations _animations; + Animation _a_selected; + + int _top = 0; + + Sets _mySets; + Sets _featuredSets; + QList _custom; + + enum class Section { + Inlines, + Gifs, + Featured, + Stickers, + }; + Section _section = Section::Stickers; + bool _setGifCommand = false; + UserData *_inlineBot; + QString _inlineBotTitle; + uint64 _lastScrolled = 0; + QTimer _updateInlineItems; + bool _inlineWithThumb = false; + + std_::unique_ptr _switchPmButton; + QString _switchPmStartToken; + + typedef QVector InlineItems; + struct InlineRow { + InlineRow() : height(0) { + } + int32 height; + InlineItems items; + }; + typedef QVector InlineRows; + InlineRows _inlineRows; + void clearInlineRows(bool resultsDeleted); + + using GifLayouts = QMap; + GifLayouts _gifLayouts; + InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position); + + using InlineLayouts = QMap; + InlineLayouts _inlineLayouts; + InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position); + + bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth); + bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false); + + InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0); + void deleteUnusedGifLayouts(); + + void deleteUnusedInlineLayouts(); + + int validateExistingInlineRows(const InlineResults &results); + void selectInlineResult(int row, int column); + void removeRecentSticker(int tab, int index); + + int _selected = -1; + int _pressed = -1; + int _selectedFeaturedSet = -1; + int _pressedFeaturedSet = -1; + int _selectedFeaturedSetAdd = -1; + int _pressedFeaturedSetAdd = -1; + QPoint _lastMousePos; + + QString _addText; + int _addWidth; + + LinkButton _settings; + + QTimer _previewTimer; + bool _previewShown = false; +}; + +class EmojiPanel : public TWidget { + Q_OBJECT + +public: + + EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY); // Stickers::NoneSetId if in emoji + void setText(const QString &text); + void setDeleteVisible(bool isVisible); + + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + + int32 wantedY() const { + return _wantedY; + } + void setWantedY(int32 y) { + _wantedY = y; + } + +signals: + + void deleteClicked(quint64 setId); + void mousePressed(); + +public slots: + + void onDelete(); + +private: + + void updateText(); + + int32 _wantedY; + QString _text, _fullText; + uint64 _setId; + bool _special, _deleteVisible; + IconedButton *_delete; + +}; + +class EmojiSwitchButton : public Button { +public: + + EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji + void paintEvent(QPaintEvent *e); + void updateText(const QString &inlineBotUsername = QString()); + +protected: + + bool _toStickers; + QString _text; + int32 _textWidth; + +}; + +} // namespace internal + +class EmojiPan : public TWidget, public RPCSender { + Q_OBJECT + +public: + EmojiPan(QWidget *parent); + + void setMaxHeight(int32 h); + void paintEvent(QPaintEvent *e); + + void moveBottom(int32 bottom, bool force = false); + + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void otherEnter(); + void otherLeave(); + + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + + bool event(QEvent *e); + + void fastHide(); + bool hiding() const { + return _hiding || _hideTimer.isActive(); + } + + void step_appearance(float64 ms, bool timer); + void step_slide(float64 ms, bool timer); + void step_icons(uint64 ms, bool timer); + + bool eventFilter(QObject *obj, QEvent *e); + void stickersInstalled(uint64 setId); + + void queryInlineBot(UserData *bot, PeerData *peer, QString query); + void clearInlineBot(); + + bool overlaps(const QRect &globalRect) { + if (isHidden() || !_cache.isNull()) return false; + + return QRect(st::dropdownDef.padding.left(), + st::dropdownDef.padding.top(), + _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), + _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() + ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + + void notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout); + void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout); + bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout); + bool ui_isInlineItemBeingChosen(); + + bool inlineResultsShown() const { + return s_inner.inlineResultsShown(); + } + +public slots: + void refreshStickers(); + void refreshSavedGifs(); + + void hideStart(); + void hideFinish(); + + void showStart(); + void onWndActiveChanged(); + + void onTabChange(); + void onScrollEmoji(); + void onScrollStickers(); + void onSwitch(); + + void onDisplaySet(quint64 setId); + void onInstallSet(quint64 setId); + void onRemoveSet(quint64 setId); + void onRemoveSetSure(); + void onDelayedHide(); + + void onRefreshIcons(); + void onRefreshPanels(); + + void onSaveConfig(); + void onSaveConfigDelayed(int32 delay); + + void onInlineRequest(); + void onEmptyInlineRows(); + +signals: + void emojiSelected(EmojiPtr emoji); + void stickerSelected(DocumentData *sticker); + void photoSelected(PhotoData *photo); + void inlineResultSelected(InlineBots::Result *result, UserData *bot); + + void updateStickers(); + +private: + bool preventAutoHide() const; + void installSetDone(const MTPmessages_StickerSetInstallResult &result); + bool installSetFail(uint64 setId, const RPCError &error); + + void paintStickerSettingsIcon(Painter &p) const; + void paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const; + + void validateSelectedIcon(bool animated = false); + void updateContentHeight(); + + void leaveToChildEvent(QEvent *e, QWidget *child); + void hideAnimated(); + void prepareShowHideCache(); + + void updateSelected(); + void updateIcons(); + + void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab); + void updatePanelsPositions(const QVector &panels, int32 st); + + void showAll(); + void hideAll(); + + int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers; + bool _horizontal = false; + + bool _noTabUpdate = false; + + int32 _width, _height, _bottom; + bool _hiding = false; + QPixmap _cache; + + anim::fvalue a_opacity = { 0. }; + Animation _a_appearance; + + QTimer _hideTimer; + + BoxShadow _shadow; + + FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols; + QList _icons; + QVector _iconHovers; + int _iconOver = -1; + int _iconSel = 0; + int _iconDown = -1; + bool _iconsDragging = false; + typedef QMap Animations; // index - showing, -index - hiding + Animations _iconAnimations; + Animation _a_icons; + QPoint _iconsMousePos, _iconsMouseDown; + int _iconsLeft = 0; + int _iconsTop = 0; + int _iconsStartX = 0; + int _iconsMax = 0; + anim::ivalue _iconsX = { 0, 0 }; + anim::ivalue _iconSelX = { 0, 0 }; + uint64 _iconsStartAnim = 0; + + bool _stickersShown = false; + bool _shownFromInlineQuery = false; + QPixmap _fromCache, _toCache; + anim::ivalue a_fromCoord, a_toCoord; + anim::fvalue a_fromAlpha, a_toAlpha; + Animation _a_slide; + + ScrollArea e_scroll; + internal::EmojiPanInner e_inner; + QVector e_panels; + internal::EmojiSwitchButton e_switch; + ScrollArea s_scroll; + internal::StickerPanInner s_inner; + QVector s_panels; + internal::EmojiSwitchButton s_switch; + + uint64 _displayingSetId = 0; + uint64 _removingSetId = 0; + + QTimer _saveConfigTimer; + + // inline bots + typedef QMap InlineCache; + InlineCache _inlineCache; + QTimer _inlineRequestTimer; + + void inlineBotChanged(); + int32 showInlineRows(bool newResults); + bool hideOnNoInlineResults(); + void recountContentMaxHeight(); + bool refreshInlineRows(int32 *added = 0); + UserData *_inlineBot = nullptr; + PeerData *_inlineQueryPeer = nullptr; + QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; + mtpRequestId _inlineRequestId = 0; + void inlineResultsDone(const MTPmessages_BotResults &result); + bool inlineResultsFail(const RPCError &error); + +}; diff --git a/Telegram/SourceFiles/stickers/stickers.cpp b/Telegram/SourceFiles/stickers/stickers.cpp new file mode 100644 index 000000000..4f5412d62 --- /dev/null +++ b/Telegram/SourceFiles/stickers/stickers.cpp @@ -0,0 +1,143 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "lang.h" + +#include "boxes/stickersetbox.h" +#include "boxes/confirmbox.h" +#include "apiwrap.h" +#include "localstorage.h" +#include "mainwidget.h" + +namespace Stickers { + +void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { + auto &v = d.vsets.c_vector().v; + auto &order = Global::RefStickerSetsOrder(); + Stickers::Order archived; + archived.reserve(v.size()); + QMap setsToRequest; + for_const (auto &stickerSet, v) { + const MTPDstickerSet *setData = nullptr; + switch (stickerSet.type()) { + case mtpc_stickerSetCovered: { + auto &d = stickerSet.c_stickerSetCovered(); + if (d.vset.type() == mtpc_stickerSet) { + setData = &d.vset.c_stickerSet(); + } + } break; + case mtpc_stickerSetMultiCovered: { + auto &d = stickerSet.c_stickerSetMultiCovered(); + if (d.vset.type() == mtpc_stickerSet) { + setData = &d.vset.c_stickerSet(); + } + } break; + } + if (setData) { + auto set = Stickers::feedSet(*setData); + if (set->stickers.isEmpty()) { + setsToRequest.insert(set->id, set->access); + } + auto index = order.indexOf(set->id); + if (index >= 0) { + order.removeAt(index); + } + archived.push_back(set->id); + } + } + if (!setsToRequest.isEmpty()) { + for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) { + App::api()->scheduleStickerSetRequest(i.key(), i.value()); + } + App::api()->requestStickerSets(); + } + Local::writeInstalledStickers(); + Local::writeArchivedStickers(); + Ui::showLayer(new StickersBox(archived), KeepOtherLayers); + + emit App::main()->stickersUpdated(); +} + +void installLocally(uint64 setId) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.end()) { + return; + } + + auto flags = it->flags; + it->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread); + it->flags |= MTPDstickerSet::Flag::f_installed; + auto changedFlags = flags ^ it->flags; + + auto &order = Global::RefStickerSetsOrder(); + int insertAtIndex = 0, currentIndex = order.indexOf(setId); + if (currentIndex != insertAtIndex) { + if (currentIndex > 0) { + order.removeAt(currentIndex); + } + order.insert(insertAtIndex, setId); + } + + auto custom = sets.find(Stickers::CustomSetId); + if (custom != sets.cend()) { + for_const (auto sticker, it->stickers) { + int removeIndex = custom->stickers.indexOf(sticker); + if (removeIndex >= 0) custom->stickers.removeAt(removeIndex); + } + if (custom->stickers.isEmpty()) { + sets.erase(custom); + } + } + Local::writeInstalledStickers(); + if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) Local::writeFeaturedStickers(); + if (changedFlags & MTPDstickerSet::Flag::f_archived) { + auto index = Global::RefArchivedStickerSetsOrder().indexOf(setId); + if (index >= 0) { + Global::RefArchivedStickerSetsOrder().removeAt(index); + Local::writeArchivedStickers(); + } + } + emit App::main()->stickersUpdated(); +} + +void undoInstallLocally(uint64 setId) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(setId); + if (it == sets.end()) { + return; + } + + it->flags &= ~MTPDstickerSet::Flag::f_installed; + + auto &order = Global::RefStickerSetsOrder(); + int currentIndex = order.indexOf(setId); + if (currentIndex >= 0) { + order.removeAt(currentIndex); + } + + Local::writeInstalledStickers(); + emit App::main()->stickersUpdated(); + + Ui::showLayer(new InformBox(lang(lng_stickers_not_found)), KeepOtherLayers); +} + +} // namespace Stickers diff --git a/Telegram/SourceFiles/stickers/stickers.h b/Telegram/SourceFiles/stickers/stickers.h new file mode 100644 index 000000000..06df73d47 --- /dev/null +++ b/Telegram/SourceFiles/stickers/stickers.h @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace Stickers { + +void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d); +void installLocally(uint64 setId); +void undoInstallLocally(uint64 setId); + +} // namespace Stickers diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style new file mode 100644 index 000000000..11168295c --- /dev/null +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -0,0 +1,39 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +using "basic.style"; + +featuredStickersHeader: 45px; +featuredStickersSkip: 15px; + +featuredStickersHeaderFont: semiboldFont; +featuredStickersHeaderFg: windowTextFg; +featuredStickersHeaderTop: 0px; +featuredStickersSubheaderFont: normalFont; +featuredStickersSubheaderFg: #777; +featuredStickersSubheaderTop: 20px; + +featuredStickersAddTop: 3px; +featuredStickersAdd: RoundButton(defaultActiveButton) { + width: -17px; + height: 26px; + textTop: 4px; + downTextTop: 5px; +} diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 9bffcf52f..e5938b127 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -38,6 +38,7 @@ '<(src_loc)/overview/overview.style', '<(src_loc)/profile/profile.style', '<(src_loc)/settings/settings.style', + '<(src_loc)/stickers/stickers.style', '<(src_loc)/ui/widgets/widgets.style', ], 'langpacks': [ @@ -377,6 +378,10 @@ '<(src_loc)/settings/settings_scale_widget.h', '<(src_loc)/settings/settings_widget.cpp', '<(src_loc)/settings/settings_widget.h', + '<(src_loc)/stickers/emoji_pan.cpp', + '<(src_loc)/stickers/emoji_pan.h', + '<(src_loc)/stickers/stickers.cpp', + '<(src_loc)/stickers/stickers.h', '<(src_loc)/ui/buttons/history_down_button.cpp', '<(src_loc)/ui/buttons/history_down_button.h', '<(src_loc)/ui/buttons/icon_button.cpp', diff --git a/Telegram/gyp/telegram_win.gypi b/Telegram/gyp/telegram_win.gypi index 1973d650c..ce23aa2e6 100644 --- a/Telegram/gyp/telegram_win.gypi +++ b/Telegram/gyp/telegram_win.gypi @@ -39,11 +39,6 @@ 'lib_exif', 'OpenAL32', 'common', - 'libavformat/libavformat.a', - 'libavcodec/libavcodec.a', - 'libavutil/libavutil.a', - 'libswresample/libswresample.a', - 'libswscale/libswscale.a', 'opus', 'celt', 'silk_common', @@ -52,6 +47,17 @@ 'lib/exception_handler', 'lib/crash_generation_client', ], + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + 'libavformat/libavformat.a', + 'libavcodec/libavcodec.a', + 'libavutil/libavutil.a', + 'libswresample/libswresample.a', + 'libswscale/libswscale.a', + ], + }, + }, 'configurations': { 'Debug': { 'include_dirs': [